Comencemos nuestro viaje SQL para comprender la agregación de datos en SQL y los tipos de agregaciones, incluidas las agregaciones simples y deslizantes.
Antes de saltar a las agregaciones, vale la pena considerar hechos interesantes que a menudo algunos desarrolladores pasan por alto cuando se trata de SQL en general y la agregación en particular.
En este artículo, SQL se refiere a T-SQL, que es la versión de SQL de Microsoft y tiene más funciones que el SQL estándar.
Matemáticas detrás de SQL
Es muy importante comprender que T-SQL se basa en algunos conceptos matemáticos sólidos, aunque no es un lenguaje matemático rígido.
Según el libro "Microsoft_SQL_Server_2008_T_SQL_Fundamentals" de Itzik Ben-Gan, SQL está diseñado para consultar y administrar datos en un sistema de administración de bases de datos relacionales (RDBMS).
El propio sistema de gestión de bases de datos relacionales se basa en dos ramas matemáticas sólidas:
- Teoría de conjuntos
- Lógica de predicados
Teoría de conjuntos
La teoría de conjuntos, como su nombre lo indica, es una rama de las matemáticas sobre conjuntos que también pueden llamarse colecciones de objetos distintos definidos.
En resumen, en la teoría de conjuntos, pensamos en cosas u objetos como un todo de la misma manera que pensamos en un elemento individual.
Por ejemplo, un libro es un conjunto de todos los libros distintos definidos, por lo tanto, tomamos un libro como un todo, lo cual es suficiente para obtener detalles de todos los libros que contiene.
Lógica de predicados
La lógica de predicados es una lógica booleana que devuelve verdadero o falso según la condición o los valores de las variables.
La lógica de predicados se puede utilizar para hacer cumplir las reglas de integridad (el precio debe ser superior a 0,00) o filtrar datos (donde el precio es superior a 10,00), sin embargo, en el contexto de T-SQL, tenemos tres valores lógicos de la siguiente manera:
- Verdadero
- Falso
- Desconocido (Nulo)
Esto se puede ilustrar de la siguiente manera:
Un ejemplo de un predicado es "Donde el precio del libro es mayor que 10,00".
Eso es suficiente sobre matemáticas, pero tenga en cuenta que me referiré a esto más adelante en el artículo.
Por qué es fácil agregar datos en SQL
Agregar datos en SQL en su forma más simple se trata de conocer los totales de una sola vez.
Por ejemplo, si tenemos una tabla de clientes que contiene una lista de todos los clientes junto con sus detalles, los datos agregados de la tabla de clientes pueden darnos el número total de clientes que tenemos.
Como se discutió anteriormente, pensamos en un conjunto como un elemento único, por lo que simplemente aplicamos una función agregada a la tabla para obtener los totales.
Dado que SQL es originalmente un lenguaje basado en conjuntos (como se mencionó anteriormente), es relativamente más fácil aplicarle funciones agregadas en comparación con otros lenguajes.
Por ejemplo, si tenemos una tabla de productos que tiene registros de todos los productos en la base de datos, podemos aplicar directamente la función de conteo a una tabla de productos para obtener el número total de productos en lugar de contarlos uno por uno en un ciclo.
Receta de agregación de datos
Para agregar datos en SQL, necesitamos lo siguiente como mínimo:
- Datos (tabla) con columnas que cuando se agregan tienen sentido
- Una función agregada que se aplicará a los datos
Preparación de datos de muestra (tabla)
Tomemos un ejemplo de una tabla de pedidos simple que contiene tres elementos (columnas):
- Número de pedido (ID de pedido)
- Fecha en que se realizó el pedido (OrderDate)
- Importe del pedido (TotalAmount)
Vamos a crear la base de datos AggregateSample para continuar:
-- Create aggregate sample database CREATE DATABASE AggregateSample
Ahora cree la tabla de pedidos en la base de datos de muestra de la siguiente manera:
-- Create order table in the aggregate sample database USE AggregateSample CREATE TABLE SimpleOrder (OrderId INT PRIMARY KEY IDENTITY(1,1), OrderDate DATETIME2, TotalAmount DECIMAL(10,2) )
Poblando datos de muestra
Complete la tabla agregando una fila:
INSERT INTO dbo.SimpleOrder ( OrderDate ,TotalAmount ) VALUES ( '20180101' -- OrderDate - datetime2 ,20.50 -- TotalAmount - decimal(10, 2) ); GO
Miremos la tabla ahora:
-- View order table SELECT OrderId ,OrderDate ,TotalAmount FROM SimpleOrder
Tenga en cuenta que estoy usando dbForge Studio para SQL Server en este artículo, por lo que solo el aspecto de salida puede diferir si ejecuta el mismo código en SSMS (SQL Server Management Studio), no hay diferencia en lo que respecta a los scripts y sus resultados.
Funciones agregadas básicas
Las funciones agregadas básicas que se pueden aplicar a la tabla son las siguientes:
- Suma
- Contar
- Mínimo
- Máximo
- Promedio
Tabla de registros únicos agregados
Ahora la pregunta interesante es, "¿podemos agregar (sumar o contar) datos (registros) en una tabla si solo tiene una fila como en nuestro caso?" La respuesta es "Sí", podemos, aunque no tiene mucho sentido, pero puede ayudarnos a comprender cómo se preparan los datos para la agregación.
Para obtener el número total de pedidos, usamos la función count() con la tabla, como se mencionó anteriormente, simplemente podemos aplicar la función de agregado a la tabla, ya que SQL es un lenguaje basado en conjuntos y las operaciones se pueden aplicar a un conjunto. directamente.
-- Getting total number of orders placed so far SELECT COUNT(*) AS Total_Orders FROM SimpleOrder
Ahora, ¿qué pasa con el pedido con un monto mínimo, máximo y promedio para un solo registro:
-- Getting order with minimum amount, maximum amount, average amount and total orders SELECT COUNT(*) AS Total_Orders ,MIN(TotalAmount) AS Min_Amount ,MAX(TotalAmount) AS Max_Amount ,AVG(TotalAmount) Average_Amount FROM SimpleOrder
Como podemos ver en la salida, la cantidad mínima, máxima y promedio es la misma si tenemos un solo registro, por lo que es posible aplicar una función agregada a un solo registro, pero nos da los mismos resultados.
Necesitamos al menos más de un registro para dar sentido a los datos agregados.
Agregar tabla de registros múltiples
Agreguemos ahora cuatro registros más de la siguiente manera:
INSERT INTO dbo.SimpleOrder ( OrderDate ,TotalAmount ) VALUES ( '20180101' -- OrderDate - datetime2 ,20.50 -- TotalAmount - decimal(10, 2) ), ( '20180102' -- OrderDate - datetime2 ,30.50 -- TotalAmount - decimal(10, 2) ), ( '20180103' -- OrderDate - datetime2 ,10.50 -- TotalAmount - decimal(10, 2) ), ( '20180110' -- OrderDate - datetime2 ,100.50 -- TotalAmount - decimal(10, 2) ); GO
La tabla ahora tiene el siguiente aspecto:
Si aplicamos ahora las funciones de agregación a la tabla, vamos a obtener buenos resultados:
-- Getting order with minimum amount, maximum amount, average amount and total orders SELECT COUNT(*) AS Total_Orders ,MIN(TotalAmount) AS Min_Amount ,MAX(TotalAmount) AS Max_Amount ,AVG(TotalAmount) Average_Amount FROM SimpleOrder
Agrupación de datos agregados
Podemos agrupar los datos agregados por cualquier columna o conjunto de columnas para obtener agregados basados en esa columna.
Por ejemplo, si queremos saber el número total de pedidos por fecha, tenemos que agrupar la tabla por fecha usando la cláusula Group by de la siguiente manera:
-- Getting total orders per date SELECT OrderDate ,COUNT(*) AS Total_Orders FROM SimpleOrder GROUP BY OrderDate
La salida es la siguiente:
Entonces, si queremos ver la suma de todo el monto del pedido, simplemente podemos aplicar la función de suma a la columna de cantidad total sin ninguna agrupación de la siguiente manera:
-- Sum of all the orders amount SELECT SUM(TotalAmount) AS Sum_of_Orders_Amount FROM SimpleOrder
Para obtener la suma de la cantidad de pedidos por fecha, simplemente agregamos grupo por fecha a la instrucción SQL anterior de la siguiente manera:
-- Sum of all the orders amount per date SELECT OrderDate ,SUM(TotalAmount) AS Sum_of_Orders FROM SimpleOrder GROUP BY OrderDate
Obtener totales sin agrupar datos
Podemos obtener totales de inmediato, como el total de pedidos, el monto máximo de pedidos, el monto mínimo de pedidos, la suma de los montos de los pedidos, el monto promedio de los pedidos sin necesidad de agruparlos si la agregación es para todas las tablas.
-- Getting order with minimum amount, maximum amount, average amount, sum of amount and total orders SELECT COUNT(*) AS Total_Orders ,MIN(TotalAmount) AS Min_Amount ,MAX(TotalAmount) AS Max_Amount ,AVG(TotalAmount) AS Average_Amount ,SUM(TotalAmount) AS Sum_of_Amount FROM SimpleOrder
Agregar Clientes a los Pedidos
Agreguemos un poco de diversión agregando clientes en nuestra mesa. Podemos hacer esto creando otra tabla de clientes y pasando la identificación del cliente a la tabla de pedidos, sin embargo, para simplificar y simular el estilo del almacén de datos (donde las tablas no están normalizadas), estoy agregando la columna del nombre del cliente en la tabla de pedidos de la siguiente manera :
-- Adding CustomerName column and data to the order table ALTER TABLE SimpleOrder ADD CustomerName VARCHAR(40) NULL GO UPDATE SimpleOrder SET CustomerName = 'Eric' WHERE OrderId = 1 GO UPDATE SimpleOrder SET CustomerName = 'Sadaf' WHERE OrderId = 2 GO UPDATE SimpleOrder SET CustomerName = 'Peter' WHERE OrderId = 3 GO UPDATE SimpleOrder SET CustomerName = 'Asif' WHERE OrderId = 4 GO UPDATE SimpleOrder SET CustomerName = 'Peter' WHERE OrderId = 5 GO
Obtención de pedidos totales por cliente
¿Puedes adivinar ahora cómo obtener el total de pedidos por cliente? Debe agrupar por cliente (CustomerName) y aplicar la función agregada count() a todos los registros de la siguiente manera:
-- Total orders per customer SELECT CustomerName,COUNT(*) AS Total_Orders FROM SimpleOrder GROUP BY CustomerName
Agregar cinco registros más a la tabla de pedidos
Ahora vamos a agregar cinco filas más a la tabla de orden simple de la siguiente manera:
-- Adding 5 more records to order table INSERT INTO SimpleOrder (OrderDate, TotalAmount, CustomerName) VALUES ('01-Jan-2018', 70.50, 'Sam'), ('02-Jan-2018', 170.50, 'Adil'), ('03-Jan-2018',50.00,'Sarah'), ('04-Jan-2018',50.00,'Asif'), ('11-Jan-2018',50.00,'Peter') GO
Echa un vistazo a los datos ahora:
-- Viewing order table after adding customer name and five more rows SELECT OrderId,CustomerName,OrderDate,TotalAmount FROM SimpleOrder GO
Obtención de pedidos totales por cliente ordenados por pedidos máximos a mínimos
Si está interesado en los pedidos totales por cliente ordenados por pedidos máximos y mínimos, no es una mala idea dividir esto en pasos más pequeños de la siguiente manera:
-- (1) Getting total orders SELECT COUNT(*) AS Total_Orders FROM SimpleOrder
-- (2) Getting total orders per customer SELECT CustomerName,COUNT(*) AS Total_Orders FROM SimpleOrder GROUP BY CustomerName
Para ordenar el recuento de pedidos de máximo a mínimo, necesitamos usar la cláusula Order By DESC (orden descendente) con count() al final de la siguiente manera:
-- (3) Getting total orders per customer from maximum to minimum orders SELECT CustomerName,COUNT(*) AS Total_Orders FROM SimpleOrder GROUP BY CustomerName ORDER BY COUNT(*) DESC
Obtención de pedidos totales por fecha ordenados primero por el pedido más reciente
Con el método anterior, ahora podemos averiguar el total de pedidos por fecha ordenados primero por el pedido más reciente de la siguiente manera:
-- Getting total orders per date from most recent first SELECT CAST(OrderDate AS DATE) AS OrderDate,COUNT(*) AS Total_Orders FROM SimpleOrder GROUP BY OrderDate ORDER BY OrderDate DESC
La función CAST nos ayuda a obtener solo la parte de la fecha. La salida es la siguiente:
Puede usar tantas combinaciones como sea posible siempre que tengan sentido.
Ejecución de agregaciones
Ahora que estamos familiarizados con la aplicación de funciones agregadas a nuestros datos, pasemos a la forma avanzada de agregaciones y una de esas agregaciones es la agregación en ejecución.
Las agregaciones en ejecución son agregaciones aplicadas a un subconjunto de datos en lugar de a todo el conjunto de datos, lo que nos ayuda a crear pequeñas ventanas en los datos.
Hasta ahora, hemos visto que todas las funciones agregadas se aplican a todas las filas de la tabla que se pueden agrupar por alguna columna, como la fecha del pedido o el nombre del cliente, pero con las agregaciones en ejecución tenemos la libertad de aplicar las funciones agregadas sin agrupar todo. conjunto de datos.
Obviamente, esto significa que podemos aplicar la función agregada sin usar la cláusula Group By, lo cual es algo extraño para los principiantes de SQL (o, a veces, algunos desarrolladores pasan esto por alto) que no están familiarizados con las funciones de ventana y la ejecución de agregaciones.
Ventanas en Datos
Como se dijo anteriormente, la agregación en ejecución se aplica a un subconjunto de conjuntos de datos o (en otras palabras) en pequeñas ventanas de datos.
Piense en las ventanas como un conjunto(s) dentro de un conjunto o una(s) mesa(s) dentro de una mesa. Un buen ejemplo de ventanas de datos en nuestro caso es que tenemos la tabla de pedidos que contiene pedidos realizados en diferentes fechas, así que si cada fecha es una ventana separada, entonces podemos aplicar funciones agregadas en cada ventana de la misma manera que aplicamos a la mesa.
Si ordenamos la tabla de pedidos (SimpleOrder) por fecha de pedido (OrderDate) de la siguiente manera:
-- View order table sorted by order date SELECT so.OrderId ,so.OrderDate ,so.TotalAmount ,so.CustomerName FROM SimpleOrder so ORDER BY so.OrderDate
Windows en datos listos para ejecutar agregaciones se puede ver a continuación:
También podemos considerar estas ventanas o subconjuntos como seis minitablas basadas en fechas de pedidos y se pueden aplicar agregados en cada una de estas minitablas.
Uso de la partición por dentro de la cláusula OVER()
Las agregaciones en ejecución se pueden aplicar mediante la partición de la tabla usando "Partición por" dentro de la cláusula OVER().
Por ejemplo, si queremos particionar la tabla de pedidos por fechas, como cada fecha es una subtabla o ventana en el conjunto de datos, entonces tenemos que particionar los datos por fecha de pedido y esto se puede lograr usando una función agregada como COUNT( ) con OVER() y Partition by inside OVER() de la siguiente manera:
-- Running Aggregation on Order table by partitioning by dates SELECT OrderDate, Total_Orders=COUNT(*) OVER(PARTITION BY OrderDate) FROM SimpleOrder
Obtención de totales acumulados por ventana de fecha (partición)
Las agregaciones en ejecución nos ayudan a limitar el alcance de la agregación solo a la ventana definida y podemos obtener totales acumulados por ventana de la siguiente manera:
-- Getting total orders, minimum amount, maximum amount, average amount and sum of all amounts per date window (partition by date) SELECT CAST (OrderDate AS DATE) AS OrderDate, Count=COUNT(*) OVER (PARTITION BY OrderDate), Min_Amount=MIN(TotalAmount) OVER (PARTITION BY OrderDate) , Max_Amount=MAX(TotalAmount) OVER (PARTITION BY OrderDate) , Average_Amount=AVG(TotalAmount) OVER (PARTITION BY OrderDate), Sum_Amount=SUM(TotalAmount) OVER (PARTITION BY OrderDate) FROM SimpleOrder
Obtención de totales acumulados por ventana de cliente (partición)
Al igual que los totales acumulados por ventana de fecha, también podemos calcular los totales acumulados por ventana de cliente dividiendo el conjunto de pedidos (tabla) en pequeños subconjuntos de clientes (particiones) de la siguiente manera:
-- Getting total orders, minimum amount, maximum amount, average amount and sum of all amounts per customer window (partition by customer) SELECT CustomerName, CAST (OrderDate AS DATE) AS OrderDate, Count=COUNT(*) OVER (PARTITION BY CustomerName), Min_Amount=MIN(TotalAmount) OVER (PARTITION BY CustomerName) , Max_Amount=MAX(TotalAmount) OVER (PARTITION BY CustomerName) , Average_Amount=AVG(TotalAmount) OVER (PARTITION BY CustomerName), Sum_Amount=SUM(TotalAmount) OVER (PARTITION BY CustomerName) FROM SimpleOrder ORDER BY Count DESC,OrderDate
Agregaciones deslizantes
Las agregaciones deslizantes son las agregaciones que se pueden aplicar a los marcos dentro de una ventana, lo que significa reducir aún más el alcance dentro de la ventana (partición).
En otras palabras, los totales acumulados nos brindan totales (suma, promedio, mínimo, máximo, conteo) para toda la ventana (subconjunto) que creamos dentro de una tabla, mientras que los totales deslizantes nos brindan totales (suma, promedio, mínimo, máximo, conteo) para el marco (subconjunto de subconjunto) dentro de la ventana (subconjunto) de la tabla.
Por ejemplo, si creamos una ventana de datos basada en el cliente (partición por cliente), podemos ver que el cliente "Pedro" tiene tres registros en su ventana y todas las agregaciones se aplican a estos tres registros. Ahora, si queremos crear un marco para dos filas solo a la vez, eso significa que la agregación se reduce aún más y luego se aplica a la primera y segunda filas y luego a la segunda y tercera filas y así sucesivamente.
Uso de ROWS PRECEEDING con Order By dentro de la cláusula OVER()
Las agregaciones deslizantes se pueden aplicar agregando FILAS
Por ejemplo, si queremos agregar datos para solo dos filas a la vez para cada cliente, entonces necesitamos que se apliquen agregaciones deslizantes a la tabla de pedidos de la siguiente manera:
-- Getting minimum amount, maximum amount, average amount per frame per customer window SELECT CustomerName, Min_Amount=Min(TotalAmount) OVER (PARTITION BY CustomerName ORDER BY OrderDate ROWS 1 PRECEDING), Max_Amount=Max(TotalAmount) OVER (PARTITION BY CustomerName ORDER BY OrderDate ROWS 1 PRECEDING) , Average_Amount=AVG(TotalAmount) OVER (PARTITION BY CustomerName ORDER BY OrderDate ROWS 1 PRECEDING) FROM SimpleOrder so ORDER BY CustomerName
Para entender cómo funciona, veamos la tabla original en el contexto de marcos y ventanas:
En la primera fila de la ventana del cliente Peter, hizo un pedido con un monto de 30,50, ya que este es el comienzo del marco dentro de la ventana del cliente, por lo que el mínimo y el máximo son iguales, ya que no hay una fila anterior con la que comparar.
A continuación, el monto mínimo sigue siendo el mismo pero el máximo se convierte en 100,50 ya que el monto de la fila anterior (primera fila) es 30,50 y el monto de esta fila es 100,50, por lo que el máximo de los dos es 100,50.
A continuación, pasando a la tercera fila, la comparación se realizará con la segunda fila por lo que la cantidad mínima de las dos es 50,00 y la cantidad máxima de las dos filas es 100,50.
Función MDX hasta la fecha (YTD) y agregaciones en ejecución
MDX es un lenguaje de expresión multidimensional que se utiliza para consultar datos multidimensionales (como cubos) y se utiliza en soluciones de inteligencia empresarial (BI).
De acuerdo con https://docs.microsoft.com/en-us/sql/mdx/ytd-mdx, la función Año hasta la fecha (YTD) en MDX funciona de la misma manera que las agregaciones en ejecución o deslizamiento. Por ejemplo, YTD, a menudo utilizado en combinación con ningún parámetro proporcionado, muestra un total acumulado hasta la fecha.
Esto significa que si aplicamos esta función en el año, nos dará todos los datos del año, pero si profundizamos hasta marzo, nos dará todos los totales desde el comienzo del año hasta marzo, y así sucesivamente.
Esto es muy útil en los informes de SSRS.
Cosas que hacer
¡Eso es todo! Está listo para hacer un análisis básico de datos después de leer este artículo y puede mejorar aún más sus habilidades con las siguientes cosas:
- Intente escribir un script de agregados en ejecución creando ventanas en otras columnas, como Monto total.
- También intente escribir un script de agregados deslizantes creando marcos en otras columnas, como Monto total.
- Puede agregar más columnas y registros a la tabla (o incluso más tablas) para probar otras combinaciones de agregación.
- Las secuencias de comandos de ejemplo mencionadas en este artículo se pueden convertir en procedimientos almacenados para usar en informes de SSRS detrás de conjuntos de datos.
Referencias:
- Ytd (MDX)
- dbForge Studio para SQL Server