Está en la página 1de 33

Fundamentos de SQL: Agrupaciones y funciones de agregación

Por campusMVP
Domina SQL a un gran nivel con este curso avanzado »

Seguimos con nuestra serie de posts sobre los fundamentos del lenguaje de consultas
SQL, pero recuerda que, si quieres aprender SQL en serio con todo el apoyo de un tutor
especialista, nuestro curso online de fundamentos de SQL es tu mejor opción para
aprender de forma rápida y eficaz.

Hasta ahora hemos visto qué es SQL, qué es una base de datos relacional y cómo
se diseñan, y hemos estudiado las consultas simples, las consultas multi-tabla,
los diferentes tipos de JOIN en las consultas multi-tabla y las operaciones con
conjuntos.

En esta ocasión vamos a estudiar cómo generar resultados de consultas agrupados y


con algunas operaciones de agregación aplicadas.

Funciones de agregación
Las funciones de agregación en SQL nos permiten efectuar operaciones sobre un
conjunto de resultados, pero devolviendo un único valor agregado para todos ellos. Es
decir, nos permiten obtener medias, máximos, etc... sobre un conjunto de valores.

Las funciones de agregación básicas que soportan todos los gestores de datos son las
siguientes:

o COUNT: devuelve el número total de filas seleccionadas por


la consulta.
o MIN: devuelve el valor mínimo del campo que
especifiquemos.
o MAX: devuelve el valor máximo del campo que
especifiquemos.
o SUM: suma los valores del campo que especifiquemos. Sólo
se puede utilizar en columnas numéricas.
o AVG: devuelve el valor promedio del campo que
especifiquemos. Sólo se puede utilizar en columnas numéricas.
Las funciones anteriores son las básicas en SQL, pero cada sistema gestor de
bases de datos relacionales ofrece su propio conjunto, más amplio, con otras
funciones de agregación particulares. Puedes consultar las que ofrecen SQL
Server, Oracle y MySQL.

Todas estas funciones se aplican a una sola columna, que especificaremos entre
paréntesis, excepto la función COUNT, que se puede aplicar a una columna o indicar un
“*”. La diferencia entre poner el nombre de una columna o un “*”, es que en el primer
caso no cuenta los valores nulos para dicha columna, y en el segundo si.
Nota: Si esta serie de artículos te está pareciendo interesante, entonces ni te
imaginas lo que puedes aprender con este curso de fundamentos de SQL.

Así, por ejemplo, si queremos obtener algunos datos agregados de la tabla de pedidos
de la base de datos de ejemplo Northwind , podemos escribir una consulta simple
como la siguiente:

SELECT COUNT(*) AS TotalFilas, COUNT(ShipRegion) AS FilasNoNulas,

MIN(ShippedDate) AS FechaMin, MAX(ShippedDate) AS FechaMax,

SUM(Freight) AS PesoTotal, AVG(Freight) PesoPromedio

FROM Orders

y obtendríamos el siguiente resultado en el entorno de pruebas:

De esta manera sabremos que existen en total 830 pedidos en la base de datos, 323
registros que tienen asignada una zona de entrega, la fecha del pedido más antiguo (el
10 de julio de 1996), la fecha del pedido más reciente (el 6 de mayo de 1998 ¡los datos
de ejemplo son muy antiguos!), el total de peso enviado entre todos los pedidos
(64.942,69 Kg o sea, más de 64 toneladas) y el peso promedio del los envíos
(78,2442Kg). No está mal para una consulta tan simple.

Como podemos observar del resultado de la consulta anterior, las funciones de


agregación devuelven una sola fila , salvo que vayan unidas a la cláusula GROUP BY,
que veremos a continuación.

Agrupando resultados
La cláusula GROUP BY unida a un SELECT permite agrupar filas según las
columnas que se indiquen como parámetros , y se suele utilizar en conjunto con las
funciones de agrupación, para obtener datos resumidos y agrupados por las
columnas que se necesiten.

Hemos visto en el ejemplo anterior que obteníamos sólo una fila con los datos
indicados correspondientes a toda la tabla . Ahora vamos a ver con otro ejemplo
cómo obtener datos correspondientes a diversos grupos de filas, concretamente
agrupados por cada empleado:

SELECT EmployeeID, COUNT(*) AS TotalPedidos, COUNT(ShipRegion) AS


FilasNoNulas,

MIN(ShippedDate) AS FechaMin, MAX(ShippedDate) AS FechaMax,

SUM(Freight) PesoTotal, AVG(Freight) PesoPromedio

FROM Orders

GROUP BY EmployeeID

En este caso obtenemos los mismos datos pero agrupándolos por empleado, de modo
que para cada empleado de la base de datos sabemos cuántos pedidos ha realizado,
cuándo fue el primero y el último, etc...:
De hecho nos resultaría muy fácil cruzarla con la tabla de empleados, usando lo
aprendido sobre consultas multi-tabla, y que se devolvieran los mismos resultados con
el nombre y los apellidos de cada empleado:

En este caso fíjate en cómo hemos usado la expresión Employees.FirstName


+ ' ' + Employees.LastName como parámetro en GROUP BY para que nos
agrupe por un campo compuesto (en SQL Server no podemos usar alias de campos
para las agrupaciones). De esta forma tenemos casi un informe preparado con una
simple consulta de agregación.

Importante: Es muy importante tener en cuenta que cuando utilizamos la


cláusula GROUP BY, los únicos campos que podemos incluir en el SELECT sin que
estén dentro de una función de agregación, son los que vayan especificados en
el GROUP BY..

La cláusula GROUP BY se puede utilizar con más de un campo al mismo tiempo. Si


indicamos más de un campo como parámetro nos devolverá la información agrupada
por los registros que tengan el mismo valor en los campos indicados.

Por ejemplo, si queremos conocer la cantidad de pedidos que cada empleado ha


enviado a través de cada transportista, podemos escribir una consulta como la
siguiente:

SELECT Employees.FirstName + ' ' + Employees.LastName AS Empleado,


Shippers.CompanyName AS Transportista,

COUNT(Orders.OrderID)AS NumPedidos

FROM Orders INNER JOIN Shippers ON Orders.ShipVia = Shippers.ShipperID

INNER JOIN Employees ON Orders.EmployeeID=Employees.EmployeeID

GROUP BY Employees.FirstName + ' ' + Employees.LastName,


Shippers.CompanyName

Con el siguiente resultado:

Así, sabremos que Andrew Fuller envió 25 pedidos con Federal Shipping, y 35 con
Federal Express.

El utilizar la cláusula GROUP BY no garantiza que los datos se devuelvan


ordenados. Suele ser una práctica recomendable incluir una cláusula ORDER
BY por las mismas columnas que utilicemos en GROUP BY, especificando el orden que
no s interese. Por ejemplo, en el caso anterior
Existe una cláusula especial, parecida a la WHERE que ya conocemos que nos permite
especificar las condiciones de filtro para los diferentes grupos de filas que devuelven
estas consultas agregadas. Esta cláusula es HAVING.

HAVING es muy similar a la cláusula WHERE, pero en vez de afectar a las filas de
la tabla, afecta a los grupos obtenidos.

Por ejemplo, si queremos repetir la consulta de pedidos por empleado de hace un rato,
pero obteniendo solamente aquellos que hayan enviado más de 5.000 Kg de producto,
y ordenados por el nombre del empleado, la consulta sería muy sencilla usando
HAVING y ORDER BY:

SELECT Employees.FirstName + ' ' + Employees.LastName AS Empleado,


COUNT(*) AS TotalPedidos,

COUNT(ShipRegion) AS FilasNoNulas,

MIN(ShippedDate) AS FechaMin, MAX(ShippedDate) AS FechaMax,

SUM(Freight) PesoTotal, AVG(Freight) PesoPromedio

FROM Orders INNER JOIN Employees ON Orders.EmployeeID =


Employees.EmployeeID

GROUP BY Employees.FirstName + ' ' + Employees.LastName

HAVING SUM(Freight) > 5000

ORDER BY Employees.FirstName + ' ' + Employees.LastName ASC

Ahora obtenemos los resultados agrupados por empleado también, pero solo aquellos
que cumplan la condición indicada (o condiciones indicadas, pues se pueden
combinar). Antes nos salían 9 empleados, y ahora solo 6 pues hay 3 cuyos envíos
totales son muy pequeños:
Ya nos falta muy poco para dominar por completo las consultas de selección de datos
en cualquier sistema gestor de bases de datos relacionales. En la próxima entrega
estudiaremos cómo realizar algunas consultas que implican el uso de sub-consultas o
que aplican algunas sintaxis especiales para utilizar subconjuntos de datos y con eso
terminaremos este bloque de fundamentos de consultas con SQL.

Agrupamiento de Registros y funciones agregadas

GROUP BY

Combina los registros con valores idénticos, en la lista de campos especificados, en


un único registro. Para cada registro se crea un valor sumario si se incluye una
función SQL agregada, como por ejemplo Sum o Count, en la instrucción
SELECT. Su sintaxis es:

SELECTcampos FROM tabla WHERE criterio GROUP BY campos del grupo

GROUP BY es opcional. Los valores de resumen se omiten si no existe una función


SQL agregada en la instrucción SELECT. Los valores Null en los campos GROUP
BY se agrupan y no se omiten. No obstante, los valores Null no se evalúan en
ninguna de las funciones SQL agregadas.

Se utiliza la cláusula WHERE para excluir aquellas filas que no desea agrupar, y la
cláusula HAVING para filtrar los registros una vez agrupados.

A menos que contenga un dato Memo u Objeto OLE , un campo de la lista de


campos GROUP BY puede referirse a cualquier campo de las tablas que aparecen
en la cláusula FROM, incluso si el campo no esta incluido en la instrucción
SELECT, siempre y cuando la instrucción SELECT incluya al menos una función
SQL agregada.

Todos los campos de la lista de campos de SELECT deben o bien incluirse en la


cláusula GROUP BY o como argumentos de una función SQL agregada.

SELECT Id_Familia, Sum(Stock)FROM Productos GROUP BY Id_Familia;

Una vez que GROUP BY ha combinado los registros, HAVING muestra cualquier
registro agrupado por la cláusula GROUP BY que satisfaga las condiciones de la
cláusula HAVING.

HAVING es similar a WHERE, determina qué registros se seleccionan. Una vez


que los registros se han agrupado utilizando GROUP BY, HAVING determina
cuales de ellos se van a mostrar.

SELECT Id_Familia Sum(Stock) FROM Productos GROUP BY Id_Familia


HAVINGSum(Stock) > 100 AND NombreProducto Like BOS*;

AVG

Calcula la media aritmética de un conjunto de valores contenidos en un campo


especificado de una consulta. Su sintaxis es la siguiente

Avg(expr)

En donde expr representa el campo que contiene los datos numéricos para los que se
desea calcular la media o una expresión que realiza un cálculo utilizando los datos de
dicho campo. La media calculada por Avg es la media aritmética (la suma de los valores
dividido por el número de valores). La función Avg no incluye ningún campo Null en el
cálculo.

SELECT Avg(Gastos) AS Promedio FROM Pedidos WHERE Gastos > 100;

Count

Calcula el número de registros devueltos por una consulta. Su sintaxis es la siguiente:

Count(expr)
En donde expr contiene el nombre del campo que desea contar. Los operandos de expr
pueden incluir el nombre de un campo de una tabla, una constante o una función (la cual
puede ser intrínseca o definida por el usuario pero no otras de las funciones agregadas
de SQL). Puede contar cualquier tipo de datos incluso texto.

Aunque expr puede realizar un cálculo sobre un campo, Count simplemente cuenta el
número de registros sin tener en cuenta qué valores se almacenan en los registros. La
función Count no cuenta los registros que tienen campos null a menos que expr sea el
carácter comodín asterisco (*). Si utiliza un asterisco, Count calcula el número total de
registros, incluyendo aquellos que contienen campos null. Count(*) es
considerablemente más rápida que Count(Campo). No se debe poner el asterisco entre
dobles comillas ('*').

SELECT Count(*) AS Total FROM Pedidos;

Si expr identifica a múltiples campos, la función Count cuenta un registro sólo si al


menos uno de los campos no es Null. Si todos los campos especificados son Null, no se
cuenta el registro. Hay que separar los nombres de los campos con ampersand (&).

SELECT Count(FechaEnvío & Transporte) AS Total FROM Pedidos;

Max, Min

Devuelven el mínimo o el máximo de un conjunto de valores contenidos en un campo


especifico de una consulta. Su sintaxis es:

Min(expr)
Max(expr)

En donde expr es el campo sobre el que se desea realizar el cálculo. Expr pueden incluir
el nombre de un campo de una tabla, una constante o una función (la cual puede ser
intrínseca o definida por el usuario pero no otras de las funciones agregadas de SQL).

SELECT Min(Gastos) AS ElMin FROM Pedidos WHERE Pais = 'España';


SELECT Max(Gastos) AS ElMax FROM Pedidos WHERE Pais = 'España';

StDev, StDevP

Devuelve estimaciones de la desviación estándar para la población (el total de los


registros de la tabla) o una muestra de la población representada (muestra aleatoria) . Su
sintaxis es:
StDev(expr)
StDevP(expr)

En donde expr representa el nombre del campo que contiene los datos que desean
evaluarse o una expresión que realiza un cálculo utilizando los datos de dichos campos.
Los operandos de expr pueden incluir el nombre de un campo de una tabla, una
constante o una función (la cual puede ser intrínseca o definida por el usuario pero no
otras de las funciones agregadas de SQL)

StDevP evalúa una población, y StDev evalúa una muestra de la población. Si la


consulta contiene menos de dos registros (o ningún registro para StDevP), estas
funciones devuelven un valor Null (el cual indica que la desviación estándar no puede
calcularse).

SELECT StDev(Gastos) AS Desviacion FROM Pedidos WHERE Pais = 'España';


SELECT StDevP(Gastos) AS Desviacion FROM Pedidos WHERE Pais = 'España';

Sum

Devuelve la suma del conjunto de valores contenido en un campo especifico de una


consulta. Su sintaxis es:

Sum(expr)

En donde expr respresenta el nombre del campo que contiene los datos que desean
sumarse o una expresión que realiza un cálculo utilizando los datos de dichos campos.
Los operandos de expr pueden incluir el nombre de un campo de una tabla, una
constante o una función (la cual puede ser intrínseca o definida por el usuario pero no
otras de las funciones agregadas de SQL).

SELECT Sum(PrecioUnidad * Cantidad) AS Total FROM DetallePedido;

Var, VarP

Devuelve una estimación de la varianza de una población (sobre el total de los registros)
o una muestra de la población (muestra aleatoria de registros) sobre los valores de un
campo. Su sintaxis es:

Var(expr)
VarP(expr)

VarP evalúa una población, y Var evalúa una muestra de la población. Expr el nombre
del campo que contiene los datos que desean evaluarse o una expresión que realiza un
cálculo utilizando los datos de dichos campos. Los operandos de expr pueden incluir el
nombre de un campo de una tabla, una constante o una función (la cual puede ser
intrínseca o definida por el usuario pero no otras de las funciones agregadas de SQL)

Si la consulta contiene menos de dos registros, Var y VarP devuelven Null (esto indica
que la varianza no puede calcularse). Puede utilizar Var y VarP en una expresión de
consulta o en una Instrucción SQL.

SELECT Var(Gastos) AS Varianza FROM Pedidos WHERE Pais = 'España';


SELECT VarP(Gastos) AS Varianza FROM Pedidos WHERE Pais = 'España';

Consultas agregadas
La cláusula GROUP BY

La clausula GROUP BY combina los registros devueltos por una


consulta SELECT obteniendo uno o varios valores agregados(suma, valor mínimo y
máximo ...).

Para cada registro se puede crear un valor agregado si se incluye una función SQL agregada,
como por ejemplo Sum o Count, en la instrucción SELECT. Su sintaxis es:

SELECT [ALL | DISTINCT ] [TOP <n> [WITH TIES]]


<nombre_campo> [{,<nombre_campo>}]
[{,<funcion_agregado>}]
FROM <nombre_tabla>|<nombre_vista>
[{,<nombre_tabla>|<nombre_vista>}]
[WHERE <condicion> [{ AND|OR <condicion>}]]
[GROUP BY <nombre_campo> [{,<nombre_campo >}]]
[HAVING <condicion>[{ AND|OR <condicion>}]]
[ORDER BY <nombre_campo>|<indice_campo> [ASC | DESC]
[{,<nombre_campo>|<indice_campo> [ASC | DESC ]}]]

Si se utiliza GROUP BY pero no existe una función SQL agregada en la


instrucción SELECT se obtiene el mismo resultado que con una consulta SELECT DISTINCT.
Los valores Null en los campos GROUP BY se agrupan y no se omiten. No obstante, los valores
Null no se evalúan en ninguna de las funciones SQL agregadas.

Todos los campos de la lista de campos de SELECT deben incluirse en la cláusula GROUP
BY o como argumentos de una función SQL agregada.

El siguiente ejemplo realiza una "cuenta" de los datos que hay en la tabla PRODUCTOS.

SELECT COUNT(*)

FROM PRODUCTOS
Este otro ejemplo, muestra la suma del PRECIO de cada uno de los productos que componen
un pedido, para calcular el total del pedido agrupados por los datos del cliente.

SELECT CLIENTES.NOMBRE,

CLIENTES.APELLIDO1,

CLIENTES.APELLIDO2,

SUM(PRECIO) -- Total del pedido

FROM DETALLE_PEDIDO

INNER JOIN PEDIDOS

ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO

INNER JOIN CLIENTES

ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE

GROUP BY CLIENTES.NOMBRE,

CLIENTES.APELLIDO1,

CLIENTES.APELLIDO2

Siempre que incluyamos una clausula WHERE en una consulta agregada esta se aplica
antes de calcular el valor agregado. Es decir, si sumamos el valor de las ventas por producto, la
suma se calcula despues de haber aplicado el filtro impuesto por la clausula WHERE.

SELECT CLIENTES.NOMBRE,

CLIENTES.APELLIDO1,

CLIENTES.APELLIDO2,

SUM(PRECIO) -- Total del pedido

FROM DETALLE_PEDIDO

INNER JOIN PEDIDOS

ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO

INNER JOIN CLIENTES


ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE

-- La clausula WHERE se aplica antes de realizar el calculo

WHERE CLIENTES.NOMBRE != 'UN NOMBRE'

GROUP BY CLIENTES.NOMBRE,

CLIENTES.APELLIDO1,

CLIENTES.APELLIDO2

La cláusula HAVING

Es posible que necesitemos calcular un agregado, pero que no necesitemos obtener todos
los datos, solo los que cumplan una condición del agregado. Por ejemplo, podemos calcular el
valor de las ventas por producto, pero que solo queramos ver los datos de los producto que
hayan vendido más o menos de una determinada cantidad. En estos casos debemos utilizar la
clausula HAVING.

Una vez que GROUP BY ha combinado los registros, HAVING muestra cualquier registro
agrupado por la cláusula GROUP BY que satisfaga las condiciones de la cláusula HAVING. Se
utiliza la cláusula WHERE para excluir aquellas filas que no desea agrupar, y la
cláusula HAVING para filtrar los registros una vez agrupados.

HAVING es similar a WHERE, determina qué registros se seleccionan pero despues de


calcular el agregado. Una vez que los registros se han agrupado utilizando GROUP
BY, HAVING determina cuales de ellos se van a mostrar. HAVING permite el uso de
funciones agregadas.

SELECT CLIENTES.NOMBRE,

CLIENTES.APELLIDO1,

CLIENTES.APELLIDO2,

SUM(PRECIO) -- Total del pedido

FROM DETALLE_PEDIDO

INNER JOIN PEDIDOS

ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO

INNER JOIN CLIENTES

ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE
-- La clausula WHERE se aplica antes de realizar el calculo

WHERE CLIENTES.NOMBRE != 'UN NOMBRE'

GROUP BY CLIENTES.NOMBRE,

CLIENTES.APELLIDO1,

CLIENTES.APELLIDO2

HAVING SUM(PRECIO) > 100

Funciones agregadas.

Transact SQL pone a nuestra disposición multiples funciones agregadas, las más comunes
son:

 MAX

 MIN

 COUNT

 SUM

 AVG

AVG

Calcula la media aritmética de un conjunto de valores contenidos en un campo especificado


de una consulta. Su sintaxis es la siguiente

AVG(<expr>)

En donde expr representa el campo que contiene los datos numéricos para los que se desea
calcular la media o una expresión que realiza un cálculo utilizando los datos de dicho campo. La
media calculada por Avg es la media aritmética (la suma de los valores dividido por el número
de valores). La función Avg no incluye ningún campo Null en el cálculo.

SELECT CLIENTES.NOMBRE,

CLIENTES.APELLIDO1,
CLIENTES.APELLIDO2,

AVG(PRECIO) -- Promedio del pedido

FROM DETALLE_PEDIDO

INNER JOIN PEDIDOS

ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO

INNER JOIN CLIENTES

ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE

GROUP BY CLIENTES.NOMBRE,

CLIENTES.APELLIDO1,

CLIENTES.APELLIDO2

Count

Calcula el número de registros devueltos por una consulta. Su sintaxis es la siguiente:

COUNT(<expr>)

En donde expr contiene el nombre del campo que desea contar. Los operandos de expr
pueden incluir el nombre de un campo de una tabla, una constante o una función (la cual
puede ser intrínseca o definida por el usuario pero no otras de las funciones agregadas de
SQL). Puede contar cualquier tipo de datos incluso texto.

Aunque expr puede realizar un cálculo sobre un campo, Count simplemente cuenta el
número de registros sin tener en cuenta qué valores se almacenan en los registros. La función
Count no cuenta los registros que tienen campos null a menos que expr sea el carácter
comodín asterisco (*). Si utiliza un asterisco, Count calcula el número total de registros,
incluyendo aquellos que contienen campos null. Count(*) es considerablemente más rápida que
Count(Campo).

SELECT COUNT(*)

FROM PEDIDOS
SELECT CLIENTES.NOMBRE, COUNT(*)

FROM PEDIDOS

INNER JOIN CLIENTES

ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE

GROUP BY CLIENTES.NOMBRE

Max, Min

Devuelven el mínimo o el máximo de un conjunto de valores contenidos en un campo


especifico de una consulta. Su sintaxis es:

MIN(<expr>)
MAX(<expr>)

En donde expr es el campo sobre el que se desea realizar el cálculo. Expr pueden incluir el
nombre de un campo de una tabla, una constante o una función (la cual puede ser intrínseca o
definida por el usuario pero no otras de las funciones agregadas de SQL).

SELECT CLIENTES.NOMBRE,

MIN(PEDIDOS.FX_ALTA),

MAX(PEDIDOS.FX_ALTA)

FROM PEDIDOS

INNER JOIN CLIENTES

ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE

GROUP BY CLIENTES.NOMBRE

Sum

Devuelve la suma del conjunto de valores contenido en un campo especifico de una


consulta. Su sintaxis es:

SUM(<expr>)
En donde expr respresenta el nombre del campo que contiene los datos que desean
sumarse o una expresión que realiza un cálculo utilizando los datos de dichos campos. Los
operandos de expr pueden incluir el nombre de un campo de una tabla, una constante o una
función (la cual puede ser intrínseca o definida por el usuario pero no otras de las funciones
agregadas de SQL).

SELECT CLIENTES.NOMBRE,

SUM(PEDIDOS.TOTAL_PEDIDO)

FROM PEDIDOS

INNER JOIN CLIENTES

ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE

GROUP BY CLIENTES.NOMBRE

Uso de Select TOP con consultas agregadas.

Podemos utilizar SELECT TOP con consultas agregadas como con cualquier otra
instruccion Transact SQL.

En estos casos, la clausula TOP se aplica despues de calcular el agregado, devolviendo las N
filas indicadas.

En este escenario es posible que queramos obtener los N valores que satisfagan una
condicion. Por ejemplo, queremos si queremos obtener los tres primeros clientes con mayores
pedidos, usariamos una consulta parecida a esta:

SELECT TOP 3
CLIENTES.NOMBRE,
SUM(DETALLE_PEDIDO.PRECIO)

FROM DETALLE_PEDIDO

INNER JOIN PEDIDOS

ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO

INNER JOIN CLIENTES

ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE

GROUP BY CLIENTES.NOMBRE
ORDER BY 2 -- SUM(DETALLE_PEDIDO.PRECIO_UNIDAD)

Sin embargo, puede darse el caso, de que el cuarto cliente devuelto por la consulta tenga
un valor agragado identico al tercero, (es decir, estan empatados). El uso de TOP
3 discriminaría el cuarto registro. Para evitar este comportamiento, y que la consulta devuelva
también al cuarto cliente utilizamos la clausula WITH TIES.

SELECT TOP 3 WITH TIES


CLIENTES.NOMBRE,
SUM(DETALLE_PEDIDO.PRECIO)

FROM DETALLE_PEDIDO

INNER JOIN PEDIDOS

ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO

INNER JOIN CLIENTES

ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE

GROUP BY CLIENTES.NOMBRE

ORDER BY 2 -- SUM(DETALLE_PEDIDO.PRECIO_UNIDAD)

Select FOR XML


Clausula FOR XML.

A partir de la version 2000 SQL Server incluye la clausula FOR XML para la consultas. Sin
embargo, es a partir de la versión 2005 cuando se integra XML como tipo de dato nativo.

Cuando especificamos la clausula FOR XML el resultado de la consulta es devuelto en


formato XML.

La clausula FOR XML admite los siguientes modos que representan el formato en el que el
XML es devuelto:

 XML AUTO, el modo AUTO emplea los campos en la declaración SELECT para formar
una jerarquía simple XML.

 XML RAW, el modo RAW genera elementos únicos, los cuales se denominan row, por
cada fila retornada.

 EXPLICIT, el modo EXPLICIT requiere un formato específico que puede ser mapeado
en casi cualquier forma XML, y al mismo tiempo ser formulado por una sola consulta SQL.
Adicionalmente, disponemos de dos opciones más TYPE y ELEMENTS que determinan el
formato del XML resultante. Los vemos con ejemplos.

Un ejemplo de XML AUTO.

SELECT CO_FAMILIA, FAMILIA

FROM FAMILIAS

ORDER BY FAMILIA

FOR XML AUTO, TYPE

Obtendremos el siguiente resultado:

<FAMILIAS CO_FAMILIA="1" FAMILIA="FAMILIA 1" />

<FAMILIAS CO_FAMILIA="2" FAMILIA="FAMILIA 2" />

<FAMILIAS CO_FAMILIA="3" FAMILIA="FAMILIA 3" />

<FAMILIAS CO_FAMILIA="4" FAMILIA="FAMILIA 4" />

Podemos obtener el resultado como elementos de la siguiente forma:

SELECT CO_FAMILIA, FAMILIA

FROM FAMILIAS

FOR XML AUTO, ELEMENTS

Obtendremos el siguiente resultado:

<FAMILIAS>

<CO_FAMILIA>1</CO_FAMILIA>

<FAMILIA>FAMILIA 1</FAMILIA>
</FAMILIAS>

<FAMILIAS>

<CO_FAMILIA>2</CO_FAMILIA>

<FAMILIA>FAMILIA 2</FAMILIA>

</FAMILIAS>

<FAMILIAS>

<CO_FAMILIA>3</CO_FAMILIA>

<FAMILIA>FAMILIA 3</FAMILIA>

</FAMILIAS>

<FAMILIAS>

<CO_FAMILIA>4</CO_FAMILIA>

<FAMILIA>FAMILIA 4</FAMILIA>

</FAMILIAS>

Ahora un ejemplo de XML RAW:

SELECT CO_FAMILIA, FAMILIA

FROM FAMILIAS

ORDER BY FAMILIA

FOR XML RAW , TYPE

Obtenemos el siguiente resultado:

<row CO_FAMILIA="1" FAMILIA="FAMILIA 1" />

<row CO_FAMILIA="2" FAMILIA="FAMILIA 2" />

<row CO_FAMILIA="3" FAMILIA="FAMILIA 3" />


<row CO_FAMILIA="4" FAMILIA="FAMILIA 4" />

Podemos obtener el resultado como elementos de la siguiente forma:

<row>

<CO_FAMILIA>1</CO_FAMILIA>

<FAMILIA>FAMILIA 1</FAMILIA>

</row>

<row>

<CO_FAMILIA>2</CO_FAMILIA>

<FAMILIA>FAMILIA 2</FAMILIA>

</row>

<row>

<CO_FAMILIA>3</CO_FAMILIA>

<FAMILIA>FAMILIA 3</FAMILIA>

</row>

<row>

<CO_FAMILIA>4</CO_FAMILIA>

<FAMILIA>FAMILIA 4</FAMILIA>

</row>

También es posible especificar el nodo que queremos que muestre:

SELECT CO_FAMILIA, FAMILIA

FROM FAMILIAS

ORDER BY FAMILIA
FOR XML RAW ('FamiliasDeProductos') , TYPE

Devuelve el siguiente resultado:

<FamiliasDeProductos CO_FAMILIA="1" FAMILIA="FAMILIA 1" />

<FamiliasDeProductos CO_FAMILIA="2" FAMILIA="FAMILIA 2" />

<FamiliasDeProductos CO_FAMILIA="3" FAMILIA="FAMILIA 3" />

<FamiliasDeProductos CO_FAMILIA="4" FAMILIA="FAMILIA 4" />

Del mismo modo con la opción ELEMENTS:

SELECT CO_FAMILIA, FAMILIA

FROM FAMILIAS

ORDER BY FAMILIA

FOR XML RAW ('FamiliasDeProductos') , ELEMENTS

Obtendremos el siguiente resultado:

<FamiliasDeProductos>

<CO_FAMILIA>1</CO_FAMILIA>

<FAMILIA>FAMILIA 1</FAMILIA>

</FamiliasDeProductos>

<FamiliasDeProductos>

<CO_FAMILIA>2</CO_FAMILIA>

<FAMILIA>FAMILIA 2</FAMILIA>

</FamiliasDeProductos>
<FamiliasDeProductos>

<CO_FAMILIA>3</CO_FAMILIA>

<FAMILIA>FAMILIA 3</FAMILIA>

</FamiliasDeProductos>

<FamiliasDeProductos>

<CO_FAMILIA>4</CO_FAMILIA>

<FAMILIA>FAMILIA 4</FAMILIA>

</FamiliasDeProductos>

Ahora un ejemplo con el formato XML EXPLICIT.

SELECT
1 AS TAG, -- La primera columna debe tener el alias TAG

NULL AS PARENT, -- La segunda columna debe tener el alias PARENT

-- El resto de columnas deben tener el alias en el formato:

-- <NombreNodo>!<nodo>!<atributo>

CO_FAMILIA as "FamiliaDeProductos!1!CODIGO_FAMILIA",

FAMILIA as "FamiliaDeProductos!1!DESCRIPCION"

FROM FAMILIAS

ORDER BY FAMILIA

FOR XML EXPLICIT

Obtenemos el siguiente resultado:

<FamiliaDeProductos CODIGO_FAMILIA="1" DESCRIPCION="FAMILIA 1" />

<FamiliaDeProductos CODIGO_FAMILIA="2" DESCRIPCION="FAMILIA 2" />


<FamiliaDeProductos CODIGO_FAMILIA="3" DESCRIPCION="FAMILIA 3" />

<FamiliaDeProductos CODIGO_FAMILIA="4" DESCRIPCION="FAMILIA 4" />

Campos y variables XML.

Dado que XML es un tipo nativo de XML podemos definir tablas con campos de tipo XML,
variables ...

El siguiente ejemplo muestra como trabajar con campos y variables XML.

-- Primero creamos una tabla con un campo XML

CREATE TABLE tablaXML

ID int not null identity,

DOC xml null,

constraint PK_tablaXML PRIMARY KEY (ID)

GO

DECLARE @xml xml -- Variable de tipo XML

-- Leemos los datos de la tabla FAMILIAS

SET @xml = (SELECT CO_FAMILIA, FAMILIA

FROM FAMILIAS FOR XML AUTO)

-- y los guardamos en nuestra tabla

INSERT INTO tablaXML

(DOC) VALUES (@xml)


-- Hacemos lo mismo con los productos

SET @xml = (SELECT *

FROM PRODUCTOS FOR XML AUTO)

INSERT INTO tablaXML

(DOC) VALUES (@xml)

-- Consultamos la tabla y vemos el resultado

SELECT * FROM tablaXML

Cuando consultemos la tabla tendremos la siguiente información (en mi caso claro!):

<!--Registro de la tabla familias-->

<FAMILIAS CO_FAMILIA="1" FAMILIA="FAMILIA 1" />

<FAMILIAS CO_FAMILIA="2" FAMILIA="FAMILIA 2" />

<FAMILIAS CO_FAMILIA="3" FAMILIA="FAMILIA 3" />

<FAMILIAS CO_FAMILIA="4" FAMILIA="FAMILIA 4" />

<!--Registro de la tabla Productos-->

<PRODUCTOS CO_PRODUCTO="1" CO_CATEGORIA="1" PRODUCTO="PRODUCTO 1" />

<PRODUCTOS CO_PRODUCTO="2" CO_CATEGORIA="1" PRODUCTO="PRODUCTO 2" />

<PRODUCTOS CO_PRODUCTO="3" CO_CATEGORIA="2" PRODUCTO="PRODUCTO 3" />

Operaciones con conjuntos.


SQL Server 2005 permite tres tipos de operaciones con conjuntos:

 UNION, disponible en todas las versiones de SQL Server.

 EXCEPT, nuevo en SQL Server 2005.

 INTERSECT, nuevo en SQL Server 2005.


Para utilizar operaciones de conjuntos debemos cumplir una serie de normas.

 Las consultas a unir deben tener el mismo número campos, y además los campos
deben ser del mismo tipo.

 Sólo puede haber una única clausula ORDER BY al final de la sentencia SELECT.

UNION

UNION devuelve la suma de dos o más conjuntos de resultados. El conjunto obtenido como
resultado de UNION tiene la misma estructura que los conjuntos originales.

El siguiente ejemplo muestra el uso de UNION

SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento

FROM EMPLEADOS

UNION

SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento

FROM CLIENTES

Cuando realizamos una consulta con UNION internamente se realiza una


operacion DISTINCT sobre el conjunto de resultados final. Si queremos obtener todos los
valores debemos utiliza UNION ALL.

SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento

FROM EMPLEADOS

UNION ALL

SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento

FROM CLIENTES

EXCEPT

EXCEPT devuelve la diferencia (resta) de dos o más conjuntos de resultados. El conjunto


obtenido como resultado de EXCEPT tiene la misma estructura que los conjuntos originales.

El siguiente ejemplo muestra el uso de EXCEPT


SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento

FROM EMPLEADOS

EXCEPT

SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento

FROM CLIENTES

El uso de EXCEPT, como norma general, es mucho más rápido que utilizar condiciones NOT
IN o EXISTS en la clausula WHERE.

INTERSECT

Devuelve la intersección entre dos o más conjuntos de resultados en uno. El conjunto


obtenido como resultado de INTERSECT tiene la misma estructura que los conjuntos
originales.

El siguiente ejemplo muestra el uso de INTERSECT

SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento

FROM EMPLEADOS

INTERSECT

SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento

FROM CLIENTES

Insertar datos en Transact SQL


Inserción individual de filas.

Para realizar la insercción individual de filas SQL posee la instrucción INSERT INTO.La
insercción individual de filas es la que más comunmente utilizaremos. Su sintaxis es la
siguiente:

INSERT INTO <nombre_tabla>


[(<campo1>[,<campo2>,...])]
values
(<valor1>,<valor2>,...);
El siguiente ejemplo muestra la inserción de un registro en la tabla PRECIOS.

INSERT INTO PRECIOS

(PRECIO, FX_INICIO, FX_FIN, CO_PRODUCTO)

VALUES

(10, getdate(),getdate()+30, 1)

Insertción múltiple de filas.

También es posible insertar en una tabla el resultado de una consulta SELECT. De este modo
se insertarán tantas filas como haya devuelto la consulta SELECT.

El siguiente ejemplo muestra la inserción multiple de filas.

INSERT INTO PRECIOS

(PRECIO,

FX_INICIO,

FX_FIN,

CO_PRODUCTO)

SELECT PRECIO_UNIDAD,

getdate(),

getdate() + 30,

CO_PRODUCTO

FROM DETALLE_PEDIDO

Inserción de valores por defecto.

También podemos forzar a que la insercción se realice con los datos por defecto establecidos
para la tabla (o null si no tienen valores por defecto).
INSERT INTO PRECIOS DEFAULT VALUES

En SQL Sever podemos marcar un campo de una tabla como autonumérico (identity),
cuando insertamos un registro en dicha tabla el valor del campo se genera automaticamente.
Para recuperar el valor generado disponemos de varios métodos:

 Utilizar la funcion @@identity, que devuelve el último valor identidad insertado por la
transaccion:

DECLARE @Codigo int

INSERT INTO PRECIOS

(PRECIO, FX_INICIO, FX_FIN, CO_PRODUCTO)

VALUES

(10, getdate(),getdate()+30, 1)

set @Codigo = @@Identity

PRINT @Codigo

El uso de @@Identity no siempre es válido, ya que al devolver el úlitmo valor identidad


insertado por la transacción, no nos garantiza que el valor haya sido insertado en la tabla que
nos interesa (por ejemplo la tabla podría tener un trigger que insertara datos en otra tabla con
campos identidad).

 En este tipo de escenarios debemos utilizar la función, SCOPE_IDENTITY.

DECLARE @Codigo int

INSERT INTO PRECIOS

(PRECIO, FX_INICIO, FX_FIN, CO_PRODUCTO)

VALUES

(10, getdate(),getdate()+30, 1)
SET @Codigo = SCOPE_IDENTITY()

PRINT @Codigo

Clausula OUTPUT

A partir de la version de SQL Server 2005 disponemos de la clausula OUTPUT para


recuperar los valores que hemos insertado. Al igual que en un trigger disponemos de las tablas
lógicas INSERTED y DELETED.

Las columnas con prefijo DELETED reflejan el valor antes de que se complete la
instrucción UPDATE o DELETE. Es decir, son una copia de los datos "antes" del cambio.

DELETED no se puede utilizar con la cláusula OUTPUT en la instrucción INSERT.

Las columnas con prefijo INSERTED reflejan el valor después de que se complete la
instrucción UPDATE o INSERT, pero antes de que se ejecuten los desencadenadores. Es decir,
son una copia de los datos "despues" del cambio.

INSERTED no se puede utilizar con la cláusula OUTPUT en la instrucción DELETE.

DECLARE @FILAS_INSERTADAS TABLE

( CO_PRECIO int,

PRECIO decimal,

FX_INICIO datetime,

FX_FIN datetime,

CO_PRODUCTO int

INSERT INTO PRECIOS

(PRECIO, FX_INICIO, FX_FIN, CO_PRODUCTO)

OUTPUT INSERTED.* INTO @FILAS_INSERTADAS

VALUES

(10, getdate(),getdate()+30, 1)
SELECT * FROM @FILAS_INSERTADAS

Funciones de agregado
Devuelven estadísticas usando campos numéricos de las tablas. Solamente
Count se puede usar con campos que no son numéricos o se puede usar
asterisco (*).

Las funciones de agregado se usan generalmente para obtener información


desde varias tablas (Ver Joins) haciendo cálculos en base a un detalle, por
ejemplo, poder calcular la cantidad de facturas o boletas generadas por un
empleados, el total de compras de un clientes, el promedio de monto de los
documentos de compra de los proveedores, etc

Las funciones de agregado son las siguientes:

AVG Devuelve el Promedio de los valores

MIN Devuelve el valor Mínimo

SUM Suma los valores

COUNT Cuenta la cantidad de celdas

STDEV Devuelve la desviación estándar Muestral

STDEVP Devuelve la desviación estándar Poblacional

VAR Devuelve la Varianza muestral

VARP Devuelve la Varianza Poblacional


MAX Devuelve el valor Máximo

Ejercicios
— Usando Northwind
use Northwind
go

— El promedio de Precios de los productos


select AVG(P.UnitPrice) As Promedio from Products as P
go
— El Precio mas alto de los productos
select Max(P.UnitPrice) As ‘Precio mayor’ from Products as P
go

— El Precio mas bajo de los productos


select Min(P.UnitPrice) As ‘Precio menor’ from Products as P
go

— Cantidad de Productos
select Count(*) As ‘Cantidad de Productos’ from Products as P
go

— Cantidad de Productos en Stock


select Sum(P.UnitsInStock) As ‘Productos en Stock’ from Products as P
go

— Valor total de los Productos en Stock


select Sum(P.UnitsInStock * P.UnitPrice) As ‘Productos en Stock’ from Products
as P
go

— Monto total vendido en el año 1997 y la cantidad de órdenes


select Sum(O.Freight) As ‘Monto total’, COUNT(O.OrderID) As ‘Cantidad’
from Orders As O where YEAR(O.OrderDate) = 1997
go

— Stock mas alto y mas bajo de productos


select Max(P.UnitsInStock) As ‘Cantidad mas alta de Productos en Stock’,
Min(P.UnitsInStock) As ‘Cantidad mas baja de Productos en Stock’
from Products as P
go

— Cantidad vendida del producto con código 23


select sum(Od.Quantity) As ‘Unidades Producto 23’
from [Order Details] As OD where ProductID = 23
go

— Cantidad de órdenes generadas y monto total vendido del Empleado


con código 3
select Count(O.OrderID) As ‘Cantidad’, SUM(O.Freight) As ‘Monto Total’
from Orders As O where EmployeeID = 3
go

También podría gustarte