SQL básico: consultas resumen

Bienvenidos de nueva cuenta al curso de SQL básico, ahora que somos todos unos expertos en el uso de consultas multitabla, vamos a explorar algunas funciones y cláusulas nuevas que nos permitirán usar la sentencia SELECT para obtener resúmenes de los datos contenidos en nuestras tablas.

Cabe aclarar que una diferencia importante entre las consultas resumen y las otras que hemos explorado hasta el momento es que en las consultas normales las filas del resultado se obtienen directamente de las filas del origen de datos y cada dato que aparece en el resultado tiene su dato correspondiente en el origen de la consulta mientras que las filas generadas por las consultas resumen no representan datos del origen sino un total calculado sobre estos datos.

Funciones de agregado

Una función de agregado  acepta un grupo de datos (por lo regular una columna de datos) como argumento, y produce un único dato que resume el grupo. Por ejemplo la función AVG() acepta una columna de datos numéricos y devuelve la media aritmética (average) de los valores contenidos en la columna.

Todas las funciones de agregado tienen una estructura parecida:

Función ([ALL|DISTINCT] expression)

 

El grupo de valores sobre el que actúa la función lo determina el resultado de la expresión que será simplemente un nombre de columna o una expresión basada en una columna o varias del origen de datos.

La palabra ALL indica que se tiene que tomar en cuenta todos los valores de la columna. Si no se especifica, este es el valor por defecto.

Mientras que la palabra DISTINCT hace que se consideren todas las repeticiones del mismo valor como uno sólo (considera valores distintos).

A excepción de la función COUNT, todas las funciones de agregado ignoran los valores NULL.

Función COUNT

Su estructura es como sigue:

COUNT ({[ALL|DISTINCT] expression | * } )

 

La expresión puede ser de cualquier tipo excepto text, image o ntext.

Como se mencionó anteriormente, esta función cuenta por defecto los valores NULL y se utiliza ALL y DISTINCT según las necesidades del programador.

Un ejemplo rápido sería el siguiente:

SELECT COUNT(Nombre) AS 'No. Proveedores' from Prov

SELECT COUNT(Nombre) AS 'No. Proveedores' from Prov

Uno pensaría que el resultado es realmente el número de proveedores que se tienen registrados en la respectiva tabla, sin embargo siempre existe un poco de error humano o algún otro contratiempo que haga que este valor no sea del todo cierto, por lo que para estar seguros que los registros contabilizados no están repetidos podemos hacer lo siguiente:

SELECT COUNT(DISTINCT Nombre) AS 'No. Proveedores' from Prov

SELECT COUNT(DISTINCT Nombre) AS 'No. Proveedores' from Prov

Como podemos notar, de alguna manera había ciertos valores repetidos y ahora realmente tenemos el número de proveedores (por nombre) que hay registrados en nuestra tabla.

Otra forma interesante de utilizar esta función es utilizando (*) en lugar de una expresión, de esta forma la función devuelve el número de filas del origen después de ejecutar la cláusula WHERE.

SELECT COUNT(*) AS 'Proveedores de Nuevo León' 

FROM Prov 

WHERE Estado='Nuevo Leon'

SELECT COUNT(*) AS 'Proveedores de Nuevo León' FROM Prov WHERE Estado='Nuevo Leon'

En este ejemplo contamos a todos los proveedores que son específicamente del estado de Nuevo León, de esta forma tenemos más opciones para diseñar estas consultas y que sean de mayor utilidad según se necesite.

 

Funciones MIN y MAX

Decidí agrupar estas 2 funciones en una sola sección ya que hacen cosas opuestas y tienen la misma estructura.

MAX/MIN ([ALL|DISTINCT] expression)

 

Mientras que la función MAX devuelve el valor máximo de la expresión, la función MIN devuelve el valor mínimo, en ambos casos sin considerar los nulos.

Ambas funciones se pueden usar con columnas numéricas, de caracteres y de datetime, pero no con columnas de bit.

A continuación, un pequeño ejemplo para cada función.


SELECT MAX(PrecioUnitario) AS 'Precio Máximo' FROM Art

SELECT MAX(PrecioUnitario) AS 'Precio Máximo' FROM Art

En este ejemplo tomamos de nuestra tabla de artículo la columna de precio unitario y consultamos el precio máximo registrado.

SELECT MIN(PrecioUnitario) AS 'Precio Mínimo' FROM Art

SELECT MIN(PrecioUnitario) AS 'Precio Mínimo' FROM Art

Ahora se trata del caso opuesto, buscando en nuestra tabla el registro del precio mínimo de los artículos.


Función SUM

SUM ([ALL|DISTINCT] expression ) 

 

Su nombre casi lo explica ya que devuelve la suma de los valores conseguidos con la expresión utilizada. En este caso solo es utilizable con columnas numéricas.

SELECT SUM(Importe) AS 'Total de Ventas' FROM Venta

SELECT SUM(Importe) AS 'Total de Ventas' FROM Venta

Se hace una sumatoria de todos los importes de todas las ventas y así se puede conseguir un total de las ventas registradas (sí, en esta base de datos de prueba hay muchos registros).

 

Función AVG

AVG ([ALL|DISTINCT] expression)

 

Esta función devuelve el promedio de los valores de un grupo, dicho esto, cabe mencionar que se omiten los valores nulos.

Las situaciones ya mencionadas con anterioridad respecto a las expresiones aplican de la misma forma para esta función.

SELECT AVG(cd.Cantidad*cd.Precio) AS 'CostoPromedio' FROM CompraD AS cd 

SELECT AVG(cd.Cantidad*cd.Precio) AS 'CostoPromedio' FROM CompraD AS cd 

Esta consulta nos permite saber el costo promedio por orden de compra y a diferencia de los ejemplos anteriores, esta vez utilizamos una expresión distinta a solo el nombre de la columna.

 

Cláusula GROUP BY (Agrupamiento de filas)

Si han prestado atención podrán haber notado que los ejemplos de las funciones se componen totales de todas las filas origen con una única fila como resultado, sin embargo, a menudo se requiere obtener totales parciales ya sea para saber qué tanto ha vendido un empleado en específico o cuál artículo es el más solicitado.

En caso de que se quiera obtener este tipo de resultados es necesario utilizar la cláusula GROUP BY.

Una consulta con una cláusula GROUP BY agrupa los datos de la tabla origen y produce una única fila resultado por cada grupo formado. Las columnas indicadas en el GROUP BY se llaman columnas de agrupación o agrupamiento.

Cuando queremos realizar una agrupación múltiple, por varias columnas, éstas se indican en la cláusula GROUP BY en el orden de mayor a menor agrupación igual que con la cláusula ORDER BY.


SELECT cd.ID,AVG(cd.Cantidad*cd.Precio) AS 'CostoPromedio'

FROM CompraD AS cd

GROUP BY cd.ID

Esta consulta nos permite saber el costo promedio por orden de compra y a diferencia de los ejemplos anteriores, esta vez utilizamos una expresión distinta a solo el nombre de la columna. Cláusula GROUP BY (Agrupamiento de filas) Si han prestado atención podrán haber notado que los ejemplos de las funciones se componen totales de todas las filas origen con una única fila como resultado, sin embargo, a menudo se requiere obtener totales parciales ya sea para saber qué tanto ha vendido un empleado en específico o cuál artículo es el más solicitado. En caso de que se quiera obtener este tipo de resultados es necesario utilizar la cláusula GROUP BY. Una consulta con una cláusula GROUP BY agrupa los datos de la tabla origen y produce una única fila resultado por cada grupo formado. Las columnas indicadas en el GROUP BY se llaman columnas de agrupación o agrupamiento. Cuando queremos realizar una agrupación múltiple, por varias columnas, éstas se indican en la cláusula GROUP BY en el orden de mayor a menor agrupación igSELECT cd.ID,AVG(cd.Cantidad*cd.Precio) AS 'CostoPromedio' FROM CompraD AS cd GROUP BY cd.ID

Utilizando parte de una consulta de ejemplo anterior, tenemos el costo promedio pero esta vez por cada orden de compra, de tal manera la información puede llegar a ser más útil.


Cláusula HAVING(Selección sobre grupos de filas)

Cuando se quiere utilizar una cláusula de selección sobre filas de origen utilizamos WHERE pero en el caso de las consultas resumen en donde manejamos grupos de filas tenemos la cláusula HAVING.

Funciona de la misma manera que WHERE pero en lugar de actuar sobre las filas del origen de datos, HAVING actúa sobre las filas del resultado.

Siempre que en una condición de selección haya una función de columna, la condición deberá incluirse en la cláusula HAVING, además, HAVING filtra filas del resultado así que sólo puede contener expresiones (nombres de columnas, expresiones, funciones…) que también pueden aparecer en la lista de selección, por lo que se aplica la regla a no olvidar:

En la cláusula HAVING un nombre de columna no puede aparecer fuera de una función de agregado si no es una columna de agrupación.


SELECT COUNT(vd.Articulo) AS ‘Artículos Registrados’,vd.ID

FROM ventaD AS vd

GROUP BY vd.ID

HAVING COUNT(vd.Articulo)  < 3

SELECT COUNT(vd.Articulo) AS 'Artículos Registrados',vd.ID FROM ventaD AS vd GROUP BY vd.ID HAVING COUNT(vd.Articulo) < 3

Con esta consulta podemos obtener no la cantidad de artículos por orden de venta sino que especificamos que solo queremos las órdenes de venta con su respectivo registro de artículos que sean menores a 3.

Sabiendo ahora la manera particular en que cada una de estas funciones y cláusulas actúa, podemos explorar una gran variedad de posibilidades al combinarlas y diseñar consultas que obtengan los resultados que deseamos.

Esto es todo por esta lección, en la siguiente nota explicaremos el tema de las subconsultas, algo interesante y que se llega a necesitar con bastante frecuencia ya que se trata de una consulta dentro de otra consulta pero eso ya lo veremos a profundidad el siguiente mes.

Si tienen alguna duda respecto al tema tratado o incluso de SQL básico en general, no duden en preguntar en los comentarios.

¡Nos leemos hasta la otra!


Referencias

https://www.w3schools.com/SQl

Documentación sobre SQL de Microsoft: https://docs.microsoft.com/en-us/sql

Imágenes de la nota: screenshots tomadas por la autora en SQL Server.

Imagen principal modificada pero inicialmente tomada de: https://www.campusmvp.es/recursos/image.axd?picture=SQL-Lenguaje_1.png

SQL básico: consultas multitablas

Bienvenidos de nueva cuenta al curso básico de SQL, si por alguna razón llegaste a esta nota al azar, te invito a seguir el curso desde el inicio de la serie aquí: SQL básico: conceptos básicos.

En esta ocasión continuaremos hablando de consultas que podemos hacer pero esta vez centrándonos en la manera en que se crean pensando en la conexión de datos entre distintas tablas.

Cabe aclarar que cuando menciono “tablas” me refiero tanto a las que se encuentran  físicamente almacenadas en la base de datos como también a las temporales e incluso a las que son resultantes de alguna vista o consulta.

 

Unión de tablas UNION

Consiste en tomar 2 tablas y por consiguiente obtener otra tabla que contenga las filas de las 2 tablas de manera que aparezcan las filas de la primera tabla seguidas de las filas de la segunda tabla.

Se debe de tener en cuenta que para hacer una unión así, ambas tablas deben tener el mismo esquema (mismo número de columnas y tipos de datos compatibles) y que luego de la unión la tabla resultante hereda los encabezados de la primer tabla.

Ahora bien, vamos directamente a un ejemplo para continuar explorando el uso de UNION en consultas:

SELECT Articulo AS 'Artículos'
FROM Art
UNION
SELECT Articulo
FROM ArtAlm

 

En el ejemplo tenemos la unión de la tabla Art con la tabla ArtAlm, tratándose de las tablas que manejan  los artículos y estos mismos en almacenes. En ambos casos se pide en la consulta los datos de los artículos y en el caso de la tabla Art, se utiliza un alias con la palabra “AS” para nombrar a la columna de otra forma de tal manera que al hacer la consulta la tabla resultante tenga ese nombre de columna.

 

Union en SQL

 

En caso de encontrarse con elementos repetidos, el sistema por defecto eliminará dichas filas. Si se necesitara obtener todas las filas, incluyendo las repeticiones, se puede utilizar “UNION ALL”, esto ocasiona que la consulta se ejecute un poco más rápido ya que el sistema no tiene que estar eliminando las repeticiones.

También se debe de tener en cuenta que se pueden combinar más de 2 tablas si es que se cumplen con todos los requerimientos  e igualmente se puede hacer uso de las cláusulas que expliqué en la segunda nota de la serie: SQL básico: consultas simples.

 

Diferencia EXCEPT

Continuando con la combinación de dos o más tablas, llegamos a la que nos permitirá hacer una operación similar a una diferencia matemática.

Por lo que la tabla resultante contiene las filas de la primera consulta que no aparecen en la segunda.

Como se trata de una operación muy sencilla de comprender, vamos a un ejemplo rápido:

SELECT Articulo
FROM Art
EXCEPT
SELECT Articulo
FROM ventaD

De nuevo vamos con la tabla Art que maneja los datos de los artículos con la tabla ventaD que maneja los datos de los detalles de las ventas que se realizan.

La estructura y condiciones como vemos son las mismas que con UNION, dos consultas separadas en este caso por la palabra EXCEPT que marca hacia que lado se efectuará la operación.

Except en SQL

Como podemos observar, la tabla resultante es simplemente de aquellos artículos que se encuentran en la tabla Art (primera consulta), pero que no se encuentran en la tabla ventaD (segunda consulta).

Este tipo de consulta puede ser útil si lo que queremos es por ejemplo averiguar los artículos que tenemos en existencia que no han sido registrados como ventas.

 

Intersección INTERSECT

Esta tiene un funcionamiento parecido a las anteriores pero en la tabla resultante tenemos las filas que están simultáneamente en las dos o más consultas que utilicemos.

SELECT Articulo
FROM Art
INTERSECT
SELECT Articulo
FROM ventaD 

Siguiendo por la misma línea que hemos estado utilizando en estos ejemplos, ahora tenemos de nueva cuenta a la tabla Art y a la tabla ventaD. En esta ocasión con la operación de intersección buscamos los artículos que se encuentran registrados a su vez en ambas tablas.

Intersect en SQL

Este tipo de consultas pueden servir como en el ejemplo, para obtener una lista de artículo que se encuentren tanto en nuestra lista de artículos usual como en las de los detalles de las ventas, es decir, los artículos de los que tenemos registradas al menos una venta.

Por lo que es bastante útil si tienes tablas con mucha información en común pero quieres depurar de alguna manera a la hora de hacer consultas.

 

Composición de tablas

Las consultas y operaciones que hemos realizado hasta ahora han sido con tablas que tuviesen el mismo esquema, sin embargo, hay ocasiones en las que se necesita obtener una tabla resultado que tenga en la misma fila datos de distintas tablas.

Por ejemplo se necesita obtener pedidos y en esa misma fila obtener algunos datos del cliente como nombre y dirección, este tipo de consultas es posible y de hecho son muy usadas.

Nos apoyaremos en la composición de tablas para poder lograr consultas de este tipo, esto consiste en obtener a partir de dos tablas cualesquiera una nueva tabla fusionando las filas de una con las filas de la otra, concatenando los esquemas de ambas tablas. Básicamente se trata de armar parejas de filas.

Ahora continuaremos con la operación a partir de la cual están definidas las demás operaciones de composición de tabla, se trata del producto cartesiano.


Producto cartesiano CROSS JOIN

Este obtiene todas las posibles concatenaciones de filas de la primera tabla con filas de la segunda tabla. Para poder utilizar el CROSS JOIN debemos indicarlo en la cláusula FROM entre los nombres de las tablas que se quieran concatenar.

SELECT a.*, vd.*
FROM Agente AS a
CROSS JOIN
ventaD as vd 

En esta ocasión utilizamos las tablas de Agente y la de ventaD, al utilizar el asterisco estamos señalando que queremos que nos traigo todas las columnas de dicha tabla y utilizamos de nueva cuenta el “AS” para indicar un alias esta vez a las tablas para poder cualificar los campos, algo que se utiliza mucho sobretodo si se están utilizando consultas multitablas.

 

Cross Join en SQL

 

Cross Join en SQL

Mis tablas tienen muchas columnas, por lo que partí el resultado en 2 imágenes, pero se puede notar que primeramente se muestran las columnas de la tabla Agente y consiguientemente de la tabla ventaD.

El CROSS JOIN no es la operación más utilizada ya que por lo regular al componer 2 tablas lo que se busca es que se haga bajo una condición de selección basada en campos de combinación y para ello es más eficiente utilizar las siguientes operaciones.

Composición interna INNER JOIN

En esta ocasión esta composición es en la que los valores de las columnas combinadas se comparan mediante un operador de comparación específico. En palabras simples, es un tipo de producto cartesiano con una condición.

Es de las operaciones más utilizadas ya que es muy común querer combinar los registros de una tabla relacionada con registros correspondientes en una tabla de referencia como pueden ser los nombres de los agentes en una respectiva venta o los datos de una factura con los datos completos de tu proveedor.

SELECT v.Mov, v.MovID, v.cliente, c.nombre, c.Direccion
FROM Venta AS v
INNER JOIN
Cte AS c
ON v.Cliente=c.Cliente

INNER JOIN en SQL

En esta consulta combinamos la tabla venta con la tabla de clientes, obtenemos los movimientos que tengamos en ventas, sus identificadores y los de los clientes y por otro lado tenemos el nombre del cliente y su dirección.

Para la condición que un INNER JOIN nos exige tenemos una igualdad (que es por lo regular lo que se utiliza) como la última línea de nuestra consulta en la que igualamos el campo de cliente en la tabla de venta con el campo de cliente de la tabla cte, lo que significa que está haciendo la relación entre ambas tablas en donde nos traiga las parejas de filas según la información de ambas tablas bajo la condición de que se esté hablando del mismo cliente.

El punto débil del INNER JOIN es que al hacer una consulta con este, no aparecerán las filas que no tienen fila correspondiente en la otra tabla.

En el caso de nuestro ejemplo no aparecen los clientes que no tengan estén especificados en ninguna venta, ni las ventas que no tengan especificado algún cliente.

Composición externa justify, RIGHT y FULL JOIN

La composición externa se expresa de manera similar al INNER JOIN ya que de igual forma se necesita una condición de combinación pero esta añade al resultado las filas que no cumplan con la condición necesariamente.

Ahora lo importante es notar el uso de las palabras LEFT y RIGHT ya que son las que indicarán la tabla de la cual se van a añadir las filas sin correspondencia. Es decir, si ponemos LEFT nos traerá todas las filas de la izquierda y si ponemos RIGHT las de la derecha. Mientras que con la palabra FULL llenamos todas nuestras bases y nos regresa como resultado todas las filas aunque no tengan correspondencia de un lado o de otro.

Para poder identificar de mejor manera las diferencias entre los tres tipos de composiciones externas, veremos el mismo ejemplo en los tres casos pero con la palabra determinante de la composición externa cambiada.

SELECT c.ID,c.FechaEmision, p.Nombre,p.Direccion,p.Estado
FROM Compra AS c
LEFT JOIN Prov AS p
ON p.Proveedor = c.Proveedor

LEFT, RIGHT y FULL JOIN en SQL

La consulta nos trae de resultado muchas filas entre las cuales incluye las de la tabla compra (izquierda) que no tienen necesariamente un referente en la tabla proveedor.

SELECT c.ID,c.FechaEmision, p.Nombre,p.Direccion,p.Estado
FROM Compra AS c
RIGHT JOIN Prov AS p
ON p.Proveedor = c.Proveedor

Composición externa LEFT, RIGHT y FULL JOIN en SQL

Este resultado es aún más claro que el anterior ya que trae todas las filas de la tabla proveedor (derecha), incluso si no tienen en correspondencia algo en la tabla compra.

SELECT c.ID,c.FechaEmision, p.Nombre,p.Direccion,p.Estado
FROM Compra AS c
FULL JOIN Prov AS p
ON p.Proveedor = c.Proveedor

Composición externa LEFT, RIGHT y FULL JOIN en SQL

En este último caso el resultado es el mismo que el anterior y lo podemos comprobar con el número de filas resultado. Pasa esto ya que todas las opciones disponibles se dan con un RIGHT JOIN y no hay nada exclusivo del LEFT JOIN. Si hubiera habido filas que tuviesen solo registros concordantes del lado izquierdo, el resultado del FULL JOIN hubiese sido más claro al juntar todos los resultados posibles.

Para finalizar con la nota me gustaría mencionar algunos consejos al momento de seleccionar el tipo de JOIN a utilizar, primeramente se debe hacer un JOIN normal con la debida condición de comparación para combinar filas y luego deberías pensar si lo que necesitamos es una composición externa o interna, en cuyo caso lo mejor sería seguir estas normas:

  • Si pueden haber filas de la primera tabla que no estén  relacionadas con filas de la segunda tabla y nos interesa que salgan en el resultado, entonces cambiamos a LEFT JOIN.
  • Si pueden haber filas de la segunda tabla que no estén  relacionadas con filas de la primera tabla y nos interesa que salgan en el resultado, entonces cambiamos a RIGHT JOIN.
  • Si necesitamos LEFT y RIGHT entonces utilizamos FULL JOIN.

Esto sería todo por la lección de esta nota, en la siguiente entrada de SQL básico trataremos el tema de las consultas resumen, las distintas funciones que se utilizan para este tipo de consultas como COUNT o AVG y las cláusulas HAVING y GROUP BY.

Si tienen alguna duda respecto al tema tratado o incluso de SQL básico en general, no duden en preguntar en los comentarios.

¡Nos leemos hasta otra!

 

Referencias

https://www.w3schools.com/SQl
Documentación sobre SQL de Microsoft: https://docs.microsoft.com/en-us/sql

Fuentes de las imágenes:

Screenshots tomadas por la autora en SQL Server.
Imagen original usada para la imagen destacada: https://blog.udemy.com/wp-content/uploads/2014/04/shutterstock_113180047.jpg