Vous êtes sur la page 1sur 27

SELECT (Transact-SQL)

Recupera filas de la base de datos y habilita la seleccin de una o varias filas o columnas de una o varias tablas en SQL Server 2012. La sintaxis completa de la instruccin SELECT es compleja, aunque las clusulas principales se pueden resumir del modo siguiente: [ WITH <common_table_expression>] SELECT select_list [ INTO new_table ] [ FROM table_source ] [ WHERE search_condition ] [ GROUP BY group_by_expression ] [ HAVING search_condition ] [ ORDER BY order_expression [ ASC | DESC ] ] Los operadores UNION, EXCEPT e INTERSECT se pueden utilizar entre consultas para combinar o comparar resultados en un conjunto de resultados.

Sintaxis
<SELECT statement> ::= [WITH <common_table_expression> [,...n]] <query_expression> [ ORDER BY { order_by_expression | column_position [ ASC | DESC ] } [ ,...n ] ] [ <FOR Clause>] [ OPTION ( <query_hint> [ ,...n ] ) ] <query_expression> ::= { <query_specification> | ( <query_expression> ) } [ { UNION [ ALL ] | EXCEPT | INTERSECT } <query_specification> | ( <query_expression> ) [...n ] ] <query_specification> ::= SELECT [ ALL | DISTINCT ] [TOP ( expression ) [PERCENT] [ WITH TIES ] ] < select_list > [ INTO new_table ] [ FROM { <table_source> } [ ,...n ] ] [ WHERE <search_condition> ] [ <GROUP BY> ] [ HAVING < search_condition > ]

Comentarios
Debido a la complejidad de la instruccin SELECT, se muestran elementos y argumentos detallados de la sintaxis de cada clusula:

WITH common_table_expression Clusula SELECT Clusula INTO FROM WHERE GROUP BY

HAVING UNION EXCEPT e INTERSECT ORDER BY Clusula FOR Clusula OPTION

El orden de las clusulas en la instruccin SELECT es importante. Se puede omitir cualquiera de las clusulas opcionales pero, cuando se utilizan, deben aparecer en el orden apropiado. Las instrucciones SELECT se permiten en las funciones definidas por el usuario solo si las listas de seleccin de estas instrucciones contienen expresiones que asignan valores a variables locales de las funciones. Un nombre de cuatro partes creado con la funcin OPENDATASOURCE como la parte de nombre de servidor se puede utilizar como origen de tabla en todos los lugares de las instrucciones SELECT donde pueda aparecer un nombre de tabla. Existen algunas restricciones sintcticas en las instrucciones SELECT relacionadas con las tablas remotas.
Orden de procesamiento lgico de la instruccin SELECT

Los pasos siguientes muestran el orden de procesamiento lgico, u orden de enlaces, de una instruccin SELECT. Este orden determina cundo los objetos definidos en un paso estn disponibles para las clusulas en pasos posteriores. Por ejemplo, si el procesador de consultas puede enlazar (obtener acceso) a las tablas o las vistas definidas en la clusula FROM, estos objetos y sus columnas estn disponibles para todos los pasos siguientes. Por el contrario, dado que la clusula SELECT es el paso 8, las clusulas anteriores no pueden hacer referencia a los alias de columna o columnas derivadas definidas en esa clusula. Sin embargo, las clusulas siguientes, tales como la clusula ORDER BY, s pueden hacer referencia. Observe que la ejecucin fsica real de la instruccin est determinada por el procesador de consultas y el orden puede variar en esta lista. 1. FROM 2. ON 3. JOIN 4. WHERE 5. GROUP BY 6. WITH CUBE o WITH ROLLUP 7. HAVING 8. SELECT 9. DISTINCT 10. ORDER BY 11. TOP

Permisos
La seleccin de datos necesita el permiso SELECT en la tabla o en la vista, que se puede heredar de un mbito superior como el permiso SELECT en el esquema o el permiso CONTROL en la tabla. O bien, se necesita pertenecer a los roles fijos de base de datos db_datareader o db_owner, o al rol fijo de servidor sysadmin. La creacin de una nueva tabla mediante SELECTINTO tambin necesita tanto el permiso CREATETABLE como el permiso ALTERSCHEMA en el esquema al que pertenece la nueva tabla.

WITH common_table_expression (Transact-SQL)


Especifica un conjunto de resultados temporal con nombre, conocido como expresin de tabla comn (CTE). Se deriva de una consulta simple y se define en el mbito de ejecucin de una sola instruccin SELECT, INSERT, UPDATE o DELETE. Esta clusula tambin se puede utilizar en una instruccin CREATE VIEW como parte de la instruccin SELECT que la define. Una expresin de tabla comn puede incluir referencias a ella misma. Esto se conoce como expresin de tabla comn recursiva. Convenciones de sintaxis de Transact-SQL
Sintaxis

[ WITH <common_table_expression> [ ,...n ] ] <common_table_expression>::= expression_name [ ( column_name [ ,...n ] ) ] AS ( CTE_query_definition )

Argumentos expression_name

Es un identificador vlido de la expresin de tabla comn. expression_name debe ser diferente del nombre de cualquier otra expresin de tabla comn definida en la misma clusula WITH <common_table_expression>, pero expression_name puede coincidir con el nombre de una vista o tabla base. Cualquier referencia a expression_name en la consulta utiliza la expresin de tabla comn y no el objeto base.

column_name

Especifica un nombre de columna en la expresin de tabla comn. No se permiten nombres duplicados en una misma definicin de CTE. El nmero de nombres de columna especificado debe coincidir con el nmero de columnas del conjunto de resultados de CTE_query_definition. La lista de nombres de columna es opcional solamente si en la definicin de la consulta se suministran nombres diferentes para todas las columnas resultantes.
CTE_query_definition

Especifica una instruccin SELECT cuyo conjunto de resultados llena la expresin de tabla comn. La instruccin SELECT de CTE_query_definition debe cumplir los mismos requisitos que en la creacin de una vista, excepto que una expresin CTE no puede definir otra expresin CTE. Para obtener ms informacin, vea la seccin Comentarios y el tema CREATE VIEW (Transact-SQL). Si se definen varios parmetros CTE_query_definition, las definiciones de consulta deben combinarse mediante uno de estos operadores de conjunto: UNION ALL, UNION, EXCEPT o INTERSECT.
Comentarios Instrucciones para crear y utilizar expresiones de tabla comunes

Las instrucciones siguientes se aplican a expresiones de tabla comunes no recursivas. Para obtener instrucciones que se aplican a expresiones de tabla comunes recursivas, vea "Instrucciones para definir y usar expresiones de tabla comunes recursivas" ms adelante.

Una expresin CTE debe ir seguida de una nica instruccin SELECT, INSERT, UPDATE o DELETE que haga referencia a una parte o a la totalidad de sus columnas. Una expresin CTE tambin se puede especificar en una instruccin CREATE VIEW como parte de la instruccin SELECT de definicin de la vista. Se pueden especificar varias definiciones de consulta de CTE en una CTE no recursiva. Las definiciones deben combinarse mediante uno de estos operadores de conjuntos: UNION ALL, UNION, INTERSECT o EXCEPT. Una expresin CTE puede hacer referencia a ella misma y a otras expresiones CTE previamente definidas en la misma clusula WITH. No se permite la referencia adelantada. No se permite especificar ms de una clusula WITH en una expresin CTE. Por ejemplo, si un argumento CTE_query_definition contiene una subconsulta, esta no puede contener ninguna clusula WITH anidada que defina otra expresin CTE. No se pueden utilizar las clusulas siguientes en la definicin de CTE_query_definition: o ORDER BY (excepto cuando se especifica una clusula TOP) o INTO

Clusula OPTION con sugerencias de consulta FOR XML FOR BROWSE Cuando se utiliza una expresin CTE en una instruccin que forma parte de un lote, la instruccin que la precede debe ir seguida de punto y coma. Una consulta que haga referencia a una CTE se puede utilizar para definir un cursor. En la expresin CTE se puede hacer referencia a tablas de servidores remotos. Cuando se ejecuta una CTE, todas las sugerencias que hagan referencia a ella pueden entrar en conflicto con otras sugerencias detectadas cuando la CTE tiene acceso a sus tablas subyacentes, de la misma manera que las sugerencias que hacen referencia a vistas en las consultas. En ese caso, la consulta devuelve un error.
o o o

Instrucciones para definir y usar expresiones de tabla comunes recursivas

Las instrucciones siguientes se aplican a la definicin de una expresin de tabla comn recursiva:

La definicin de la CTE recursiva debe contener al menos dos definiciones de consulta de CTE, un miembro no recursivo y un miembro recursivo. Se pueden definir varios miembros no recursivos y recursivos, aunque todas las definiciones de consultas de miembros no recursivos deben colocarse delante de la primera definicin de miembro recursivo. Todas las definiciones de consulta de CTEson miembros no recursivos a menos que hagan referencia a la propia CTE. Los miembros no recursivos deben combinarse mediante uno de estos operadores de conjuntos: UNION ALL, UNION, INTERSECT o EXCEPT. UNION ALL es el nico operador de conjuntos permitido entre el ltimo miembro no recursivo y el primer miembro recursivo, y si se combinan varios miembros recursivos. El nmero de columnas de los miembros no recursivo y recursivo debe coincidir. El tipo de datos de una columna del miembro recursivo debe ser igual al tipo de datos de la columna correspondiente en el miembro no recursivo. La clusula FROM de un miembro recursivo solo debe hacer referencia una vez a expression_name de CTE. No se permiten los siguientes elementos en el parmetro CTE_query_definition de un miembro recursivo: o SELECT DISTINCT o GROUP BY o PIVOT (cuando el nivel de compatibilidad de la base de datos sea 110. Vea Cambios recientes en las caractersticas del Motor de base de datos de SQL Server 2012). o HAVING o Agregacin escalar o TOP o LEFT, RIGHT, OUTER JOIN (se permite INNER JOIN) o Subconsultas

Una sugerencia aplicada a una referencia recursiva a una CTE dentro de CTE_query_definition.

Las instrucciones siguientes se aplican al uso de una expresin de tabla comn recursiva:

Todas las columnas devueltas por la expresin CTE recursiva aceptan valores NULL independientemente de la nulabilidad de las columnas devueltas por las instrucciones SELECT participantes. Una expresin CTE formada incorrectamente puede generar un bucle infinito. Por ejemplo, si la definicin de la consulta del miembro recursivo devuelve los mismos valores para las columnas primarias y secundarias, se crea un bucle infinito. Para evitar que se genere un bucle infinito, se puede limitar el nmero de niveles de recursividad permitidos para una instruccin determinada mediante el uso de la sugerencia MAXRECURSION y un valor de 0 a 32.767 en la clusula OPTION de la instruccin INSERT, UPDATE, DELETE o SELECT. De esta manera, se puede controlar la ejecucin de la instruccin hasta que se resuelva el problema de cdigo que genera el bucle. El valor predeterminado de todo el servidor es 100. Cuando se especifica 0, no se aplica ningn lmite. Solo se puede especificar un valor de MAXRECURSION por instruccin. Para obtener ms informacin, vea Sugerencias de consulta (Transact-SQL). No se puede utilizar una vista que contenga una expresin de tabla comn recursiva para actualizar datos. Se pueden definir cursores en las consultas que utilicen expresiones CTE. La expresin CTE es el argumento de select_statement que define el conjunto de resultados del cursor. En el caso de las CTE recursivas nicamente se permiten los cursores de solo avance rpido y estticos (de instantnea). Si se especifica otro tipo de cursor en una CTE recursiva, el tipo de cursor se convierte a esttico. En la expresin CTE se puede hacer referencia a tablas de servidores remotos. Si se hace referencia al servidor remoto en el miembro recursivo de la CTE, se crea una cola para cada tabla remota de manera que se pueda tener acceso local a las tablas repetidas veces. Si es una consulta de CTE, aparecer Index Spool/Lazy Spools en el plan de consulta y tendr el predicado adicional WITH STACK. Esta es una forma de confirmar la recursividad apropiada. Las funciones analticas y de agregado de la parte recursiva del CTE se aplican al conjunto para el nivel de recursividad actual y no al conjunto para el CTE. Las funciones como ROW_NUMBER solo funcionan en el subconjunto de datos que les pasa el nivel de recursividad actual y no en todo el conjunto de datos pasados a la parte recursiva de la CTE. Para obtener ms informacin, vea J. Using analytical functions in a recursive CTE.

Ejemplos A.Crear una expresin de tabla comn simple

En el siguiente ejemplo se muestra el nmero total de pedidos de venta por ao para cada representante de ventas en Adventure Works Cycles.

Transact-SQL
USE AdventureWorks2012; GO -- Define the CTE expression name and column list. WITH Sales_CTE (SalesPersonID, SalesOrderID, SalesYear) AS -- Define the CTE query. ( SELECT SalesPersonID, SalesOrderID, YEAR(OrderDate) AS SalesYear FROM Sales.SalesOrderHeader WHERE SalesPersonID IS NOT NULL ) -- Define the outer query referencing the CTE name. SELECT SalesPersonID, COUNT(SalesOrderID) AS TotalSales, SalesYear FROM Sales_CTE GROUP BY SalesYear, SalesPersonID ORDER BY SalesPersonID, SalesYear; GO

B.Usar una expresin de tabla comn para limitar recuentos y promedios de informes

En el siguiente ejemplo se muestra el nmero medio de pedidos de venta correspondiente a todos los aos para los representantes de ventas.
Transact-SQL
WITH Sales_CTE (SalesPersonID, NumberOfOrders) AS ( SELECT SalesPersonID, COUNT(*) FROM Sales.SalesOrderHeader WHERE SalesPersonID IS NOT NULL GROUP BY SalesPersonID ) SELECT AVG(NumberOfOrders) AS "Average Sales Per Person" FROM Sales_CTE; GO

C.Usar una expresin de tabla comn recursiva para mostrar varios niveles de recursividad

En el ejemplo siguiente se muestra la lista jerrquica de los directivos y de los empleados que tienen a su cargo. En el ejemplo se empieza creando y rellenando la tabla dbo.MyEmployees.
Transact-SQL

-- Create an Employee table. CREATE TABLE dbo.MyEmployees ( EmployeeID smallint NOT NULL, FirstName nvarchar(30) NOT NULL, LastName nvarchar(40) NOT NULL, Title nvarchar(50) NOT NULL, DeptID smallint NOT NULL, ManagerID int NULL, CONSTRAINT PK_EmployeeID PRIMARY KEY CLUSTERED (EmployeeID ASC) ); -- Populate the table with values. INSERT INTO dbo.MyEmployees VALUES (1, N'Ken', N'Snchez', N'Chief Executive Officer',16,NULL) ,(273, N'Brian', N'Welcker', N'Vice President of Sales',3,1) ,(274, N'Stephen', N'Jiang', N'North American Sales Manager',3,273) ,(275, N'Michael', N'Blythe', N'Sales Representative',3,274) ,(276, N'Linda', N'Mitchell', N'Sales Representative',3,274) ,(285, N'Syed', N'Abbas', N'Pacific Sales Manager',3,273) ,(286, N'Lynn', N'Tsoflias', N'Sales Representative',3,285) ,(16, N'David',N'Bradley', N'Marketing Manager', 4, 273) ,(23, N'Mary', N'Gibson', N'Marketing Specialist', 4, 16);

Transact-SQL
USE AdventureWorks2012; GO WITH DirectReports(ManagerID, EmployeeID, Title, EmployeeLevel) AS ( SELECT ManagerID, EmployeeID, Title, 0 AS EmployeeLevel FROM dbo.MyEmployees WHERE ManagerID IS NULL UNION ALL SELECT e.ManagerID, e.EmployeeID, e.Title, EmployeeLevel + 1 FROM dbo.MyEmployees AS e INNER JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID ) SELECT ManagerID, EmployeeID, Title, EmployeeLevel FROM DirectReports ORDER BY ManagerID; GO

D.Usar una expresin de tabla comn recursiva para mostrar dos niveles de recursividad

En el ejemplo siguiente se muestran los directivos y los empleados que tienen a su cargo. El nmero de niveles devueltos est limitado a dos.
Transact-SQL
USE AdventureWorks2012;

GO WITH DirectReports(ManagerID, EmployeeID, Title, EmployeeLevel) AS ( SELECT ManagerID, EmployeeID, Title, 0 AS EmployeeLevel FROM dbo.MyEmployees WHERE ManagerID IS NULL UNION ALL SELECT e.ManagerID, e.EmployeeID, e.Title, EmployeeLevel + 1 FROM dbo.MyEmployees AS e INNER JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID ) SELECT ManagerID, EmployeeID, Title, EmployeeLevel FROM DirectReports WHERE EmployeeLevel <= 2 ; GO

E.Usar una expresin de tabla comn recursiva para mostrar una lista jerrquica

El ejemplo siguiente, que est basado en el ejemplo C, agrega los nombres del encargado y de los empleados, y sus cargos respectivos. La jerarqua de directivos y empleados se enfatiza de forma adicional mediante la aplicacin de sangras a cada nivel.
Transact-SQL
USE AdventureWorks2012; GO WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort) AS (SELECT CONVERT(varchar(255), e.FirstName + ' ' + e.LastName), e.Title, e.EmployeeID, 1, CONVERT(varchar(255), e.FirstName + ' ' + e.LastName) FROM dbo.MyEmployees AS e WHERE e.ManagerID IS NULL UNION ALL SELECT CONVERT(varchar(255), REPLICATE ('| ' , EmployeeLevel) + e.FirstName + ' ' + e.LastName), e.Title, e.EmployeeID, EmployeeLevel + 1, CONVERT (varchar(255), RTRIM(Sort) + '| ' + FirstName + ' ' + LastName) FROM dbo.MyEmployees AS e JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID ) SELECT EmployeeID, Name, Title, EmployeeLevel FROM DirectReports ORDER BY Sort; GO

F.Usar MAXRECURSION para cancelar una instruccin

MAXRECURSION se puede utilizar para impedir que una CTE recursiva con formato incorrecto entre en un bucle infinito. En el ejemplo siguiente se crea un bucle infinito intencionadamente y se utiliza la sugerencia MAXRECURSION para limitar el nmero de niveles de recursividad a dos.
USE AdventureWorks2012; GO --Creates an infinite loop WITH cte (EmployeeID, ManagerID, Title) as ( SELECT EmployeeID, ManagerID, Title FROM dbo.MyEmployees WHERE ManagerID IS NOT NULL UNION ALL SELECT cte.EmployeeID, cte.ManagerID, cte.Title FROM cte JOIN dbo.MyEmployees AS e ON cte.ManagerID = e.EmployeeID ) --Uses MAXRECURSION to limit the recursive levels to 2 SELECT EmployeeID, ManagerID, Title FROM cte OPTION (MAXRECURSION 2); GO

Despus de corregir el error de cdigo, ya no se requiere MAXRECURSION.En el siguiente ejemplo se muestra el cdigo corregido.
USE AdventureWorks2012; GO WITH cte (EmployeeID, ManagerID, Title) AS ( SELECT EmployeeID, ManagerID, Title FROM dbo.MyEmployees WHERE ManagerID IS NOT NULL UNION ALL SELECT e.EmployeeID, e.ManagerID, e.Title FROM dbo.MyEmployees AS e JOIN cte ON e.ManagerID = cte.EmployeeID ) SELECT EmployeeID, ManagerID, Title FROM cte; GO

G.Usar una expresin de tabla comn para recorrer selectivamente y paso a paso una relacin recursiva en una instruccin SELECT

En el ejemplo siguiente se muestra la jerarqua de ensamblados y componentes de producto necesarios para fabricar la bicicleta correspondiente a ProductAssemblyID = 800.

Transact-SQL
USE AdventureWorks2012; GO WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS ( SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty, b.EndDate, 0 AS ComponentLevel FROM Production.BillOfMaterials AS b WHERE b.ProductAssemblyID = 800 AND b.EndDate IS NULL UNION ALL SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty, bom.EndDate, ComponentLevel + 1 FROM Production.BillOfMaterials AS bom INNER JOIN Parts AS p ON bom.ProductAssemblyID = p.ComponentID AND bom.EndDate IS NULL ) SELECT AssemblyID, ComponentID, Name, PerAssemblyQty, EndDate, ComponentLevel FROM Parts AS p INNER JOIN Production.Product AS pr ON p.ComponentID = pr.ProductID ORDER BY ComponentLevel, AssemblyID, ComponentID; GO

H.Usar una CTE recursiva en una instruccin UPDATE

En el siguiente ejemplo se actualiza el valor de PerAssemblyQty para todos los componentes que se utilizan para fabricar el producto 'Road-550-W Yellow, 44' (ProductAssemblyID800). La expresin de tabla comn devuelve una lista jerrquica de los elementos que se utilizan para fabricar ProductAssemblyID 800 y los componentes que se utilizan para crear esos elementos, etc. Solo se modifican las filas devueltas por la expresin de tabla comn.
Transact-SQL
USE AdventureWorks2012; GO WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS ( SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty, b.EndDate, 0 AS ComponentLevel FROM Production.BillOfMaterials AS b WHERE b.ProductAssemblyID = 800 AND b.EndDate IS NULL UNION ALL SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty, bom.EndDate, ComponentLevel + 1 FROM Production.BillOfMaterials AS bom

INNER JOIN Parts AS p ON bom.ProductAssemblyID = p.ComponentID AND bom.EndDate IS NULL ) UPDATE Production.BillOfMaterials SET PerAssemblyQty = c.PerAssemblyQty * 2 FROM Production.BillOfMaterials AS c JOIN Parts AS d ON c.ProductAssemblyID = d.AssemblyID WHERE d.ComponentLevel = 0;

I.Usar varios miembros no recursivos y recursivos

En el ejemplo siguiente se utilizan varios miembros no recursivos y recursivos para devolver todos los antecesores de una persona especificada. Se crea una tabla y se insertan valores en ella para establecer la genealoga familiar devuelta por la CTE recursiva.
Transact-SQL
-- Genealogy table IF OBJECT_ID('dbo.Person','U') IS NOT NULL DROP TABLE dbo.Person; GO CREATE TABLE dbo.Person(ID int, Name varchar(30), Mother int, Father int); GO INSERT dbo.Person VALUES(1, 'Sue', NULL, NULL) ,(2, 'Ed', NULL, NULL) ,(3, 'Emma', 1, 2) ,(4, 'Jack', 1, 2) ,(5, 'Jane', NULL, NULL) ,(6, 'Bonnie', 5, 4) ,(7, 'Bill', 5, 4); GO -- Create the recursive CTE to find all of Bonnie's ancestors. WITH Generation (ID) AS ( -- First anchor member returns Bonnie's mother. SELECT Mother FROM dbo.Person WHERE Name = 'Bonnie' UNION -- Second anchor member returns Bonnie's father. SELECT Father FROM dbo.Person WHERE Name = 'Bonnie' UNION ALL -- First recursive member returns male ancestors of the previous generation. SELECT Person.Father FROM Generation, Person WHERE Generation.ID=Person.ID UNION ALL

-- Second recursive member returns female ancestors of the previous generation. SELECT Person.Mother FROM Generation, dbo.Person WHERE Generation.ID=Person.ID ) SELECT Person.ID, Person.Name, Person.Mother, Person.Father FROM Generation, dbo.Person WHERE Generation.ID = Person.ID; GO

J.Utilizar funciones analticas en una CTE recursiva

En el siguiente ejemplo se muestra un error que puede producirse al utilizar una funcin analtica o de agregado en la parte recursiva de una CTE.
DECLARE @t1 TABLE (itmID int, itmIDComp int); INSERT @t1 VALUES (1,10), (2,10); DECLARE @t2 TABLE (itmID int, itmIDComp int); INSERT @t2 VALUES (3,10), (4,10); WITH vw AS ( SELECT itmIDComp, itmID FROM @t1 UNION ALL SELECT itmIDComp, itmID FROM @t2 ) ,r AS ( SELECT t.itmID AS itmIDComp , NULL AS itmID ,CAST(0 AS bigint) AS N ,1 AS Lvl FROM (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) AS t (itmID) UNION ALL SELECT t.itmIDComp , t.itmID , ROW_NUMBER() OVER(PARTITION BY t.itmIDComp ORDER BY t.itmIDComp, t.itmID) AS N , Lvl + 1 FROM r JOIN vw AS t ON t.itmID = r.itmIDComp ) SELECT Lvl, N FROM r;

Los siguientes resultados son los esperados para la consulta.


Lvl 1 1 1 1 2 2 2 2 N 0 0 0 0 4 3 2 1

Los siguientes resultados son los resultados reales de la consulta.


Lvl 1 1 1 1 2 2 2 2 N 0 0 0 0 1 1 1 1

N devuelve 1 para cada paso de la parte recursiva del CTE, porque solo el subconjunto de datos para ese nivel de recursividad se pasa a ROWNUMBER.Por cada iteracin de la parte recursiva de la consulta solo se pasa una fila a ROWNUMBER.

Ejemplos de SELECT (Transact-SQL)


En este tema se proporcionan ejemplos del uso de la instruccin SELECT. A.Usar SELECT para recuperar filas y columnas En el siguiente ejemplo se muestran tres fragmentos de cdigo. En el primer ejemplo de cdigo, se devuelven todas las filas (no se especifica la clusula WHERE) y todas las columnas (con *) de la tabla Product de la base de datos AdventureWorks2012 . Transact-SQL
USE AdventureWorks2012; GO SELECT * FROM Production.Product ORDER BY Name ASC; -- Alternate way. USE AdventureWorks2012; GO SELECT p.*

FROM Production.Product AS p ORDER BY Name ASC; GO

En este ejemplo se devuelven todas las filas (no se ha especificado la clusula WHERE) y solo un subconjunto de las columnas (Name, ProductNumber, ListPrice) de la tabla Product de la base de datos AdventureWorks2012 . Adems, se agrega un encabezado de columna. Transact-SQL
USE AdventureWorks2012; GO SELECT Name, ProductNumber, ListPrice AS Price FROM Production.Product ORDER BY Name ASC; GO

En este ejemplo solo se devuelven las filas de Product que tienen una lnea de productos de R y cuyo valor correspondiente a los das para fabricar es inferior a 4. Transact-SQL
USE AdventureWorks2012; GO SELECT Name, ProductNumber, ListPrice AS Price FROM Production.Product WHERE ProductLine = 'R' AND DaysToManufacture < 4 ORDER BY Name ASC; GO

B.Usar SELECT con encabezados de columna y clculos En los siguientes ejemplos se devuelven todas las filas de la tabla Product. En el primer ejemplo se devuelven las ventas totales y los descuentos de cada producto. En el segundo ejemplo se calculan los beneficios totales de cada producto. Transact-SQL
USE AdventureWorks2012; GO SELECT p.Name AS ProductName, NonDiscountSales = (OrderQty * UnitPrice), Discounts = ((OrderQty * UnitPrice) * UnitPriceDiscount) FROM Production.Product AS p INNER JOIN Sales.SalesOrderDetail AS sod ON p.ProductID = sod.ProductID ORDER BY ProductName DESC; GO

sta es la consulta que calcula el beneficio de cada producto de cada pedido de venta. Transact-SQL
USE AdventureWorks2012; GO SELECT 'Total income is', ((OrderQty * UnitPrice) * (1.0 UnitPriceDiscount)), ' for ', p.Name AS ProductName FROM Production.Product AS p INNER JOIN Sales.SalesOrderDetail AS sod ON p.ProductID = sod.ProductID ORDER BY ProductName ASC; GO

C.Usar DISTINCT con SELECT En el siguiente ejemplo se utiliza DISTINCT para evitar la recuperacin de ttulos duplicados. Transact-SQL
USE AdventureWorks2012; GO SELECT DISTINCT JobTitle FROM HumanResources.Employee ORDER BY JobTitle; GO

D.Crear tablas con SELECT INTO En el primer ejemplo se crea una tabla temporal denominada #Bicycles en tempdb. Transact-SQL
USE tempdb; GO IF OBJECT_ID (N'#Bicycles',N'U') IS NOT NULL DROP TABLE #Bicycles; GO SELECT * INTO #Bicycles FROM AdventureWorks2012.Production.Product WHERE ProductNumber LIKE 'BK%'; GO

En el segundo ejemplo se crea la tabla permanente NewProducts. Transact-SQL


USE AdventureWorks2012; GO IF OBJECT_ID('dbo.NewProducts', 'U') IS NOT NULL

DROP TABLE dbo.NewProducts; GO ALTER DATABASE AdventureWorks2012 SET RECOVERY BULK_LOGGED; GO SELECT * INTO dbo.NewProducts FROM Production.Product WHERE ListPrice > $25 AND ListPrice < $100; GO ALTER DATABASE AdventureWorks2012 SET RECOVERY FULL; GO

E.Usar subconsultas correlacionadas En el siguiente ejemplo se muestran consultas que son semnticamente equivalentes y se demuestra la diferencia entre la utilizacin de la palabra clave EXISTS y la palabra clave IN. Ambos son ejemplos de subconsultas vlidas que recuperan una instancia de cada nombre de producto cuyo modelo es un jersey de manga larga con logotipo y cuyos nmeros de ProductModelID coinciden en las tablas Product y ProductModel. Transact-SQL
USE AdventureWorks2012; GO SELECT DISTINCT Name FROM Production.Product AS p WHERE EXISTS (SELECT * FROM Production.ProductModel AS pm WHERE p.ProductModelID = pm.ProductModelID AND pm.Name LIKE 'Long-Sleeve Logo Jersey%'); GO -- OR USE AdventureWorks2012; GO SELECT DISTINCT Name FROM Production.Product WHERE ProductModelID IN (SELECT ProductModelID FROM Production.ProductModel WHERE Name LIKE 'Long-Sleeve Logo Jersey%'); GO

En el siguiente ejemplo se utiliza IN en una subconsulta correlativa o repetitiva. Se trata de una consulta que depende de la consulta externa de sus valores. Se ejecuta varias veces, una vez por cada fila que pueda seleccionar la consulta externa. Esta consulta recupera una instancia del nombre y apellido de cada empleado cuya bonificacin en la tabla SalesPerson

sea de 5000.00 y cuyos nmeros de identificacin coincidan en las tablas Employee y SalesPerson. Transact-SQL
USE AdventureWorks2012; GO SELECT DISTINCT p.LastName, p.FirstName FROM Person.Person AS p JOIN HumanResources.Employee AS e ON e.BusinessEntityID = p.BusinessEntityID WHERE 5000.00 IN (SELECT Bonus FROM Sales.SalesPerson AS sp WHERE e.BusinessEntityID = sp.BusinessEntityID); GO

La subconsulta anterior de esta instruccin no se puede evaluar independientemente de la consulta externa. Necesita el valor Employee.EmployeeID, aunque este valor cambia a medida que el Motor de base de datos de SQL Server examina diferentes filas de Employee. Una subconsulta correlativa se puede usar tambin en la clusula HAVING de una consulta externa. En este ejemplo se buscan los modelos cuyo precio mximo es superior al doble de la media del modelo. Transact-SQL
USE AdventureWorks2012; GO SELECT p1.ProductModelID FROM Production.Product AS p1 GROUP BY p1.ProductModelID HAVING MAX(p1.ListPrice) >= ALL (SELECT AVG(p2.ListPrice) FROM Production.Product AS p2 WHERE p1.ProductModelID = p2.ProductModelID); GO

En este ejemplo se utilizan dos subconsultas correlativas para buscar los nombres de los empleados que han vendido un producto especfico. Transact-SQL
USE AdventureWorks2012; GO SELECT DISTINCT pp.LastName, pp.FirstName FROM Person.Person pp JOIN HumanResources.Employee e ON e.BusinessEntityID = pp.BusinessEntityID WHERE pp.BusinessEntityID IN (SELECT SalesPersonID FROM Sales.SalesOrderHeader WHERE SalesOrderID IN

(SELECT SalesOrderID FROM Sales.SalesOrderDetail WHERE ProductID IN (SELECT ProductID FROM Production.Product p WHERE ProductNumber = 'BK-M68B-42'))); GO

F.Usar GROUP BY En este ejemplo se busca el total de cada pedido de venta de la base de datos. Transact-SQL
USE AdventureWorks2012; GO SELECT SalesOrderID, SUM(LineTotal) AS SubTotal FROM Sales.SalesOrderDetail GROUP BY SalesOrderID ORDER BY SalesOrderID; GO

Debido a la clusula GROUP BY, solo se devuelve una fila que contiene la suma de todas las ventas por cada pedido de venta. G.Usar GROUP BY con varios grupos En este ejemplo se busca el precio medio y la suma de las ventas anuales hasta la fecha, agrupados por Id. de producto e Id. de oferta especial. Transact-SQL
USE AdventureWorks2012; GO SELECT ProductID, SpecialOfferID, AVG(UnitPrice) AS [Average Price], SUM(LineTotal) AS SubTotal FROM Sales.SalesOrderDetail GROUP BY ProductID, SpecialOfferID ORDER BY ProductID; GO

H.Usar GROUP BY y WHERE En el siguiente ejemplo se colocan los resultados en grupos despus de recuperar nicamente las filas con precios superiores a $1000. Transact-SQL
USE AdventureWorks2012; GO SELECT ProductModelID, AVG(ListPrice) AS [Average List Price]

FROM Production.Product WHERE ListPrice > $1000 GROUP BY ProductModelID ORDER BY ProductModelID; GO

I.Usar GROUP BY con una expresin En este ejemplo se agrupa por una expresin. Puede agrupar por una expresin si sta no incluye funciones de agregado. Transact-SQL
USE AdventureWorks2012; GO SELECT AVG(OrderQty) AS [Average Quantity], NonDiscountSales = (OrderQty * UnitPrice) FROM Sales.SalesOrderDetail GROUP BY (OrderQty * UnitPrice) ORDER BY (OrderQty * UnitPrice) DESC; GO

J.Usar GROUP BY con ORDER BY En este ejemplo se busca el precio medio de cada tipo de producto y se ordenan los resultados por precio medio. Transact-SQL
USE AdventureWorks2012; GO SELECT ProductID, AVG(UnitPrice) AS [Average Price] FROM Sales.SalesOrderDetail WHERE OrderQty > 10 GROUP BY ProductID ORDER BY AVG(UnitPrice); GO

K.Usar la clusula HAVING En el primer ejemplo se muestra una clusula HAVING con una funcin de agregado. Agrupa las filas de la tabla SalesOrderDetail por Id. de producto y elimina aquellos productos cuyas cantidades de pedido medias son cinco o menos. En el segundo ejemplo se muestra una clusula HAVING sin funciones de agregado. Transact-SQL
USE AdventureWorks2012; GO SELECT ProductID FROM Sales.SalesOrderDetail GROUP BY ProductID

HAVING AVG(OrderQty) > 5 ORDER BY ProductID; GO

En esta consulta se utiliza la clusula LIKE en la clusula HAVING.


USE AdventureWorks2012 ; GO SELECT SalesOrderID, CarrierTrackingNumber FROM Sales.SalesOrderDetail GROUP BY SalesOrderID, CarrierTrackingNumber HAVING CarrierTrackingNumber LIKE '4BD%' ORDER BY SalesOrderID ; GO

L.Usar HAVING y GROUP BY En el siguiente ejemplo se muestra el uso de las clusulas GROUP BY, HAVING, WHERE y ORDER BY en una instruccin SELECT. Genera grupos y valores de resumen pero lo hace tras eliminar los productos cuyos precios superan los 25 $ y cuyas cantidades de pedido medias son inferiores a 5. Tambin organiza los resultados por ProductID. Transact-SQL
USE AdventureWorks2012; GO SELECT ProductID FROM Sales.SalesOrderDetail WHERE UnitPrice < 25.00 GROUP BY ProductID HAVING AVG(OrderQty) > 5 ORDER BY ProductID; GO

M.Usar HAVING con SUM y AVG En el siguiente ejemplo se agrupa la tabla SalesOrderDetail por Id. de producto y solo se incluyen aquellos grupos de productos cuyos pedidos suman ms de $1000000.00 y cuyas cantidades de pedido medias son inferiores a 3. Transact-SQL
USE AdventureWorks2012; GO SELECT ProductID, AVG(OrderQty) AS AverageQuantity, SUM(LineTotal) AS Total FROM Sales.SalesOrderDetail GROUP BY ProductID HAVING SUM(LineTotal) > $1000000.00 AND AVG(OrderQty) < 3; GO

Para ver los productos cuyas ventas totales son superiores a $2000000.00, utilice esta consulta: Transact-SQL
USE AdventureWorks2012; GO SELECT ProductID, Total = SUM(LineTotal) FROM Sales.SalesOrderDetail GROUP BY ProductID HAVING SUM(LineTotal) > $2000000.00; GO

Si desea asegurarse de que hay al menos mil quinientos elementos para los clculos de cada producto, use HAVING COUNT(*) > 1500 para eliminar los productos que devuelven totales inferiores a 1500 elementos vendidos. La consulta sera la siguiente: Transact-SQL
USE AdventureWorks2012; GO SELECT ProductID, SUM(LineTotal) AS Total FROM Sales.SalesOrderDetail GROUP BY ProductID HAVING COUNT(*) > 1500; GO

N.Usar la sugerencia del optimizador INDEX En el ejemplo siguiente se muestran dos formas de usar la sugerencia del optimizador INDEX. En el primer ejemplo se muestra cmo obligar al optimizador a que use un ndice no clster para recuperar filas de una tabla, mientras que en el segundo ejemplo se obliga a realizar un recorrido de tabla mediante un ndice igual a 0. Transact-SQL
USE AdventureWorks2012; GO SELECT pp.FirstName, pp.LastName, e.NationalIDNumber FROM HumanResources.Employee AS e WITH (INDEX(AK_Employee_NationalIDNumber)) JOIN Person.Person AS pp on e.BusinessEntityID = pp.BusinessEntityID WHERE LastName = 'Johnson'; GO -- Force a table scan by using INDEX = 0. USE AdventureWorks2012; GO SELECT pp.LastName, pp.FirstName, e.JobTitle FROM HumanResources.Employee AS e WITH (INDEX = 0) JOIN Person.Person AS pp ON e.BusinessEntityID = pp.BusinessEntityID WHERE LastName = 'Johnson';

GO

M.Usar OPTION y las sugerencias GROUP En el ejemplo siguiente se muestra cmo se usa la clusula OPTION (GROUP) con una clusula GROUP BY. Transact-SQL
USE AdventureWorks2012; GO SELECT ProductID, OrderQty, SUM(LineTotal) AS Total FROM Sales.SalesOrderDetail WHERE UnitPrice < $5.00 GROUP BY ProductID, OrderQty ORDER BY ProductID, OrderQty OPTION (HASH GROUP, FAST 10); GO

O.Usar la sugerencia de consulta UNION En el ejemplo siguiente se usa la sugerencia de consulta MERGE UNION. Transact-SQL
USE AdventureWorks2012; GO SELECT BusinessEntityID, JobTitle, HireDate, VacationHours, SickLeaveHours FROM HumanResources.Employee AS e1 UNION SELECT BusinessEntityID, JobTitle, HireDate, VacationHours, SickLeaveHours FROM HumanResources.Employee AS e2 OPTION (MERGE UNION); GO

P.Usar una instruccin UNION simple En el ejemplo siguiente, el conjunto de resultados incluye el contenido de las columnas ProductModelID y Name de las tablas ProductModel y Gloves. Transact-SQL
USE AdventureWorks2012; GO IF OBJECT_ID ('dbo.Gloves', 'U') IS NOT NULL DROP TABLE dbo.Gloves; GO -- Create Gloves table. SELECT ProductModelID, Name INTO dbo.Gloves FROM Production.ProductModel

WHERE ProductModelID IN (3, 4); GO -- Here is the simple union. USE AdventureWorks2012; GO SELECT ProductModelID, Name FROM Production.ProductModel WHERE ProductModelID NOT IN (3, 4) UNION SELECT ProductModelID, Name FROM dbo.Gloves ORDER BY Name; GO

Q.Usar SELECT INTO con UNION


En el ejemplo siguiente, la clusula INTO de la segunda instruccin SELECT especifica que la tabla denominada ProductResults contiene el conjunto final de resultados de la unin de las columnas designadas de las tablas ProductModel y Gloves. Tenga en cuenta que la tabla Gloves se crea en la primera instruccin SELECT. Transact-SQL
USE AdventureWorks2012; GO IF OBJECT_ID ('dbo.ProductResults', 'U') IS NOT NULL DROP TABLE dbo.ProductResults; GO IF OBJECT_ID ('dbo.Gloves', 'U') IS NOT NULL DROP TABLE dbo.Gloves; GO -- Create Gloves table. SELECT ProductModelID, Name INTO dbo.Gloves FROM Production.ProductModel WHERE ProductModelID IN (3, 4); GO USE AdventureWorks2012; GO SELECT ProductModelID, Name INTO dbo.ProductResults FROM Production.ProductModel WHERE ProductModelID NOT IN (3, 4) UNION SELECT ProductModelID, Name FROM dbo.Gloves; GO SELECT ProductModelID, Name FROM dbo.ProductResults;

R.Usar UNION con dos instrucciones SELECT y ORDER BY


El orden de algunos parmetros empleados con la clusula UNION es importante. En el ejemplo siguiente se muestra el uso correcto e incorrecto de UNION en dos instrucciones SELECT en las que se va a cambiar el nombre de una columna en el resultado. Transact-SQL
USE AdventureWorks2012; GO IF OBJECT_ID ('dbo.Gloves', 'U') IS NOT NULL DROP TABLE dbo.Gloves; GO -- Create Gloves table. SELECT ProductModelID, Name INTO dbo.Gloves FROM Production.ProductModel WHERE ProductModelID IN (3, 4); GO /* INCORRECT */ USE AdventureWorks2012; GO SELECT ProductModelID, Name FROM Production.ProductModel WHERE ProductModelID NOT IN (3, 4) ORDER BY Name UNION SELECT ProductModelID, Name FROM dbo.Gloves; GO /* CORRECT */ USE AdventureWorks2012; GO SELECT ProductModelID, Name FROM Production.ProductModel WHERE ProductModelID NOT IN (3, 4) UNION SELECT ProductModelID, Name FROM dbo.Gloves ORDER BY Name; GO

S.Usar UNION de tres instrucciones SELECT para mostrar los efectos de ALL y los parntesis En los siguientes ejemplos se utiliza UNION para combinar los resultados de tres tablas que tienen las mismas 5 filas de datos. En el primer ejemplo se utiliza UNION ALL para mostrar los registros duplicados y se devuelven las 15 filas. En el segundo ejemplo se utiliza UNION sin ALL para eliminar las filas duplicadas de los resultados combinados de las tres instrucciones SELECT y se devuelven 5 filas.

En el tercer ejemplo se utiliza ALL con el primer UNION y los parntesis incluyen al segundo UNION que no utiliza ALL. El segundo UNION se procesa en primer lugar porque se encuentra entre parntesis. Devuelve 5 filas porque no se utiliza la opcin ALL y se quitan los duplicados. Estas 5 filas se combinan con los resultados del primer SELECT mediante las palabras clave UNION ALL. Esto no quita los duplicados entre los dos conjuntos de 5 filas. El resultado final es de 10 filas. Transact-SQL
USE AdventureWorks2012; GO IF OBJECT_ID ('dbo.EmployeeOne', 'U') IS NOT NULL DROP TABLE dbo.EmployeeOne; GO IF OBJECT_ID ('dbo.EmployeeTwo', 'U') IS NOT NULL DROP TABLE dbo.EmployeeTwo; GO IF OBJECT_ID ('dbo.EmployeeThree', 'U') IS NOT NULL DROP TABLE dbo.EmployeeThree; GO SELECT pp.LastName, pp.FirstName, e.JobTitle INTO dbo.EmployeeOne FROM Person.Person AS pp JOIN HumanResources.Employee AS e ON e.BusinessEntityID = pp.BusinessEntityID WHERE LastName = 'Johnson'; GO SELECT pp.LastName, pp.FirstName, e.JobTitle INTO dbo.EmployeeTwo FROM Person.Person AS pp JOIN HumanResources.Employee AS e ON e.BusinessEntityID = pp.BusinessEntityID WHERE LastName = 'Johnson'; GO SELECT pp.LastName, pp.FirstName, e.JobTitle INTO dbo.EmployeeThree FROM Person.Person AS pp JOIN HumanResources.Employee AS e ON e.BusinessEntityID = pp.BusinessEntityID WHERE LastName = 'Johnson'; GO -- Union ALL SELECT LastName, FirstName, JobTitle FROM dbo.EmployeeOne UNION ALL SELECT LastName, FirstName ,JobTitle FROM dbo.EmployeeTwo UNION ALL SELECT LastName, FirstName,JobTitle FROM dbo.EmployeeThree; GO SELECT LastName, FirstName,JobTitle FROM dbo.EmployeeOne UNION SELECT LastName, FirstName, JobTitle FROM dbo.EmployeeTwo UNION

SELECT LastName, FirstName, JobTitle FROM dbo.EmployeeThree; GO SELECT LastName, FirstName,JobTitle FROM dbo.EmployeeOne UNION ALL ( SELECT LastName, FirstName, JobTitle FROM dbo.EmployeeTwo UNION SELECT LastName, FirstName, JobTitle FROM dbo.EmployeeThree ); GO

Vous aimerez peut-être aussi