Commençons notre parcours SQL pour comprendre l'agrégation de données dans SQL et les types d'agrégations, y compris les agrégations simples et glissantes.
Avant de passer aux agrégations, il convient de considérer des faits intéressants souvent manqués par certains développeurs en ce qui concerne SQL en général et l'agrégation en particulier.
Dans cet article, SQL fait référence à T-SQL qui est la version Microsoft de SQL et a plus de fonctionnalités que le SQL standard.
Les maths derrière SQL
Il est très important de comprendre que T-SQL est basé sur des concepts mathématiques solides bien qu'il ne s'agisse pas d'un langage mathématique rigide.
Selon le livre "Microsoft_SQL_Server_2008_T_SQL_Fundamentals" par Itzik Ben-Gan, SQL est conçu pour interroger et gérer des données dans un système de gestion de base de données relationnelle (RDBMS).
Le système de gestion de bases de données relationnelles lui-même repose sur deux solides branches mathématiques :
- Théorie des ensembles
- Logique des prédicats
Théorie des ensembles
La théorie des ensembles, comme son nom l'indique, est une branche des mathématiques sur les ensembles qui peuvent aussi être appelées collections d'objets distincts définis.
En bref, dans la théorie des ensembles, nous pensons aux choses ou aux objets dans leur ensemble de la même manière que nous pensons à un élément individuel.
Par exemple, un livre est un ensemble de tous les livres distincts définis, donc, nous prenons un livre dans son ensemble, ce qui est suffisant pour obtenir des détails sur tous les livres qu'il contient.
Logique des prédicats
La logique de prédicat est une logique booléenne qui renvoie vrai ou faux selon la condition ou les valeurs des variables.
La logique de prédicat peut être utilisée pour appliquer des règles d'intégrité (le prix doit être supérieur à 0,00) ou filtrer les données (où le prix est supérieur à 10,00), cependant, dans le contexte de T-SQL, nous avons trois valeurs logiques comme suit :
- Vrai
- Faux
- Inconnu (Nul)
Ceci peut être illustré comme suit :
Un exemple de prédicat est "Où le prix du livre est supérieur à 10,00".
C'en est assez des maths, mais gardez à l'esprit que je vais y faire référence plus tard dans l'article.
Pourquoi l'agrégation de données en SQL est facile
L'agrégation de données en SQL dans sa forme la plus simple consiste à connaître les totaux en une seule fois.
Par exemple, si nous avons une table des clients qui contient une liste de tous les clients avec leurs détails, les données agrégées de la table des clients peuvent nous donner le nombre total de clients que nous avons.
Comme indiqué précédemment, nous considérons un ensemble comme un élément unique, nous appliquons donc simplement une fonction d'agrégation au tableau pour obtenir les totaux.
Étant donné que SQL est à l'origine un langage basé sur des ensembles (comme indiqué précédemment), il est donc relativement plus facile de lui appliquer des fonctions d'agrégation par rapport à d'autres langages.
Par exemple, si nous avons une table de produits qui contient des enregistrements de tous les produits de la base de données, nous pouvons immédiatement appliquer la fonction de comptage à une table de produits pour obtenir le nombre total de produits plutôt que de les compter un par un dans une boucle.
Recette d'agrégation de données
Afin d'agréger des données en SQL, nous avons besoin au minimum des éléments suivants :
- Données (tableau) avec des colonnes qui, une fois agrégées, ont un sens
- Une fonction d'agrégation à appliquer sur les données
Préparation des exemples de données (tableau)
Prenons un exemple de table de commande simple qui contient trois éléments (colonnes) :
- Numéro de commande (OrderId)
- Date à laquelle la commande a été passée (OrderDate)
- Montant de la commande (TotalAmount)
Créons la base de données AggregateSample pour aller plus loin :
-- Create aggregate sample database CREATE DATABASE AggregateSample
Créez maintenant la table de commandes dans l'exemple de base de données comme suit :
-- 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) )
Remplir des exemples de données
Remplissez le tableau en ajoutant une ligne :
INSERT INTO dbo.SimpleOrder ( OrderDate ,TotalAmount ) VALUES ( '20180101' -- OrderDate - datetime2 ,20.50 -- TotalAmount - decimal(10, 2) ); GO
Regardons le tableau maintenant :
-- View order table SELECT OrderId ,OrderDate ,TotalAmount FROM SimpleOrder
Veuillez noter que j'utilise dbForge Studio pour SQL Server dans cet article, donc seul l'aspect de la sortie peut différer si vous exécutez le même code dans SSMS (SQL Server Management Studio), il n'y a aucune différence en ce qui concerne les scripts et leurs résultats.
Fonctions d'agrégation de base
Les fonctions d'agrégation de base pouvant être appliquées au tableau sont les suivantes :
- Somme
- Compter
- Min
- Maximum
- Moyenne
Tableau d'agrégation d'un seul enregistrement
Maintenant, la question intéressante est la suivante :"pouvons-nous agréger (additionner ou compter) des données (enregistrements) dans une table si elle ne comporte qu'une seule ligne, comme dans notre cas ?" La réponse est "Oui", nous le pouvons, même si cela n'a pas beaucoup de sens, mais cela peut nous aider à comprendre comment les données se préparent pour l'agrégation.
Afin d'obtenir le nombre total de commandes, nous utilisons la fonction count () avec la table, comme indiqué précédemment, nous pouvons simplement appliquer la fonction d'agrégation à la table car SQL est un langage basé sur un ensemble et les opérations peuvent être appliquées à un ensemble directement.
-- Getting total number of orders placed so far SELECT COUNT(*) AS Total_Orders FROM SimpleOrder
Maintenant, qu'en est-il de la commande avec un montant minimum, maximum et moyen pour un seul enregistrement :
-- 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
Comme nous pouvons le voir sur la sortie, le montant minimum, maximum et moyen est le même si nous avons un seul enregistrement, donc appliquer une fonction d'agrégation à un seul enregistrement est possible mais cela nous donne les mêmes résultats.
Nous avons besoin d'au moins plusieurs enregistrements pour donner un sens aux données agrégées.
Tableau d'agrégation de plusieurs enregistrements
Ajoutons maintenant quatre autres enregistrements comme suit :
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
Le tableau se présente maintenant comme suit :
Si nous appliquons les fonctions d'agrégation à la table maintenant, nous allons obtenir de bons résultats :
-- 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
Regroupement de données agrégées
Nous pouvons regrouper les données agrégées par n'importe quelle colonne ou ensemble de colonnes pour obtenir des agrégats basés sur cette colonne.
Par exemple, si nous voulons connaître le nombre total de commandes par date, nous devons regrouper le tableau par date en utilisant la clause Group by comme suit :
-- Getting total orders per date SELECT OrderDate ,COUNT(*) AS Total_Orders FROM SimpleOrder GROUP BY OrderDate
Le résultat est le suivant :
Donc, si nous voulons voir la somme de tout le montant de la commande, nous pouvons simplement appliquer la fonction somme à la colonne du montant total sans aucun regroupement comme suit :
-- Sum of all the orders amount SELECT SUM(TotalAmount) AS Sum_of_Orders_Amount FROM SimpleOrder
Afin d'obtenir la somme du montant des commandes par date, nous ajoutons simplement le groupe par date à l'instruction SQL ci-dessus comme suit :
-- Sum of all the orders amount per date SELECT OrderDate ,SUM(TotalAmount) AS Sum_of_Orders FROM SimpleOrder GROUP BY OrderDate
Obtenir des totaux sans regrouper les données
Nous pouvons immédiatement obtenir des totaux tels que le total des commandes, le montant maximum de la commande, le montant minimum de la commande, la somme du montant des commandes, le montant moyen de la commande sans avoir besoin de le regrouper si l'agrégation est destinée à toutes les tables.
-- 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
Ajouter des clients aux commandes
Ajoutons un peu de plaisir en ajoutant des clients dans notre tableau. Nous pouvons le faire en créant une autre table de clients et en transmettant l'identifiant du client à la table des commandes, mais pour rester simple et se moquer du style d'entrepôt de données (où les tables sont dénormalisées), j'ajoute la colonne du nom du client dans la table des commandes comme suit :
-- 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
Obtenir le nombre total de commandes par client
Pouvez-vous maintenant deviner comment obtenir le nombre total de commandes par client ? Vous devez regrouper par client (CustomerName) et appliquer la fonction d'agrégation count() à tous les enregistrements comme suit :
-- Total orders per customer SELECT CustomerName,COUNT(*) AS Total_Orders FROM SimpleOrder GROUP BY CustomerName
Ajout de cinq enregistrements supplémentaires à la table de commande
Nous allons maintenant ajouter cinq lignes supplémentaires à la table de commande simple comme suit :
-- 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
Jetez un œil aux données maintenant :
-- Viewing order table after adding customer name and five more rows SELECT OrderId,CustomerName,OrderDate,TotalAmount FROM SimpleOrder GO
Obtenir le nombre total de commandes par client, trié du maximum au minimum de commandes
Si vous êtes intéressé par le nombre total de commandes par client triées par commandes maximales à minimales, ce n'est pas du tout une mauvaise idée de diviser cela en étapes plus petites comme suit :
-- (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
Pour trier le nombre de commandes du maximum au minimum, nous devons utiliser la clause Order By DESC (ordre décroissant) avec count() à la fin comme suit :
-- (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
Obtenir le nombre total de commandes par date, trié d'abord par la commande la plus récente
En utilisant la méthode ci-dessus, nous pouvons maintenant connaître le nombre total de commandes par date, triées d'abord par la commande la plus récente, comme suit :
-- 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 fonction CAST nous aide à obtenir uniquement la partie date. Le résultat est le suivant :
Vous pouvez utiliser autant de combinaisons que possible tant qu'elles ont du sens.
Exécuter des agrégations
Maintenant que nous sommes familiarisés avec l'application de fonctions d'agrégation à nos données, passons à la forme avancée des agrégations et l'une de ces agrégations est l'agrégation en cours d'exécution.
Les agrégations en cours sont les agrégations appliquées à un sous-ensemble de données plutôt qu'à l'ensemble de données, ce qui nous aide à créer de petites fenêtres sur les données.
Jusqu'à présent, nous avons vu que toutes les fonctions d'agrégation sont appliquées à toutes les lignes de la table qui peuvent être regroupées par une colonne telle que la date de commande ou le nom du client, mais avec des agrégations en cours, nous avons la liberté d'appliquer les fonctions d'agrégation sans regrouper l'ensemble jeu de données.
Évidemment, cela signifie que nous pouvons appliquer la fonction d'agrégation sans utiliser la clause Group By, ce qui est quelque peu étrange pour les débutants en SQL (ou parfois certains développeurs l'ignorent) qui ne sont pas familiers avec les fonctions de fenêtrage et l'exécution d'agrégations.
Windows sur les données
Comme indiqué précédemment, l'agrégation en cours est appliquée à un sous-ensemble de données ou (en d'autres termes) à de petites fenêtres de données.
Considérez les fenêtres comme un ensemble (s) dans un ensemble ou une (des) table (s) dans une table. Un bon exemple de fenêtrage sur les données dans notre cas est que nous avons la table de commandes qui contient des commandes passées à des dates différentes, alors que se passe-t-il si chaque date est une fenêtre distincte, alors nous pouvons appliquer des fonctions d'agrégation sur chaque fenêtre de la même manière que nous avons appliqué à le tableau.
Si nous trions la table de commande (SimpleOrder) par date de commande (OrderDate) comme suit :
-- View order table sorted by order date SELECT so.OrderId ,so.OrderDate ,so.TotalAmount ,so.CustomerName FROM SimpleOrder so ORDER BY so.OrderDate
Les fenêtres sur les données prêtes pour l'exécution des agrégations peuvent être consultées ci-dessous :
Nous pouvons également considérer ces fenêtres ou sous-ensembles car six mini-tables basées sur la date de commande et des agrégats peuvent être appliqués sur chacune de ces mini-tables.
Utilisation de la partition par dans la clause OVER()
Les agrégations en cours d'exécution peuvent être appliquées en partitionnant la table à l'aide de "Partitionner par" dans la clause OVER().
Par exemple, si nous voulons partitionner la table de commande par dates telles que chaque date est une sous-table ou une fenêtre sur un ensemble de données, nous devons partitionner les données par date de commande et cela peut être réalisé en utilisant une fonction d'agrégation telle que COUNT( ) avec OVER() et Partition by inside OVER() comme suit :
-- Running Aggregation on Order table by partitioning by dates SELECT OrderDate, Total_Orders=COUNT(*) OVER(PARTITION BY OrderDate) FROM SimpleOrder
Fenêtre Obtention des totaux cumulés par date (Partition)
Les agrégations en cours nous aident à limiter la portée de l'agrégation à la fenêtre définie uniquement et nous pouvons obtenir les totaux cumulés par fenêtre comme suit :
-- 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
Obtenir les totaux cumulés par fenêtre client (partition)
Tout comme les totaux cumulés par fenêtre de date, nous pouvons également calculer les totaux cumulés par fenêtre client en partitionnant l'ensemble de commandes (table) en sous-ensembles de petits clients (partitions) comme suit :
-- 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
Agrégations glissantes
Les agrégations glissantes sont les agrégations qui peuvent être appliquées aux cadres d'une fenêtre, ce qui signifie réduire davantage la portée dans la fenêtre (partition).
En d'autres termes, les totaux cumulés nous donnent des totaux (somme, moyenne, min, max, nombre) pour toute la fenêtre (sous-ensemble) que nous créons dans une table, tandis que les totaux glissants nous donnent des totaux (somme, moyenne, min, max, nombre) pour le cadre (sous-ensemble de sous-ensemble) dans la fenêtre (sous-ensemble) de la table.
Par exemple, si nous créons une fenêtre sur les données basée sur le client (partition par client), nous pouvons voir que le client "Pierre" a trois enregistrements dans sa fenêtre et toutes les agrégations sont appliquées à ces trois enregistrements. Maintenant, si nous voulons créer un cadre pour deux lignes seulement à la fois, cela signifie que l'agrégation est encore réduite et qu'elle est ensuite appliquée aux première et deuxième lignes, puis aux deuxième et troisième lignes et ainsi de suite.
Utilisation de ROWS PRECEEDING avec Order By dans la clause OVER()
Des agrégations glissantes peuvent être appliquées en ajoutant ROWS
Par exemple, si nous voulons agréger les données pour seulement deux lignes à la fois pour chaque client, nous avons besoin d'agrégations glissantes à appliquer à la table des commandes comme suit :
-- 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
Afin de comprendre comment cela fonctionne, regardons le tableau d'origine dans le contexte des cadres et des fenêtres :
Dans la première ligne de la fenêtre du client Peter, il a passé une commande d'un montant de 30,50 car il s'agit du début du cadre dans la fenêtre du client, donc min et max sont les mêmes car il n'y a pas de ligne précédente à comparer.
Ensuite, le montant minimum reste le même mais le maximum devient 100,50 car le montant de la ligne précédente (première ligne) est de 30,50 et ce montant de ligne est de 100,50, donc le maximum des deux est de 100,50.
Ensuite, en passant à la troisième ligne, la comparaison aura lieu avec la deuxième ligne de sorte que le montant minimum des deux est de 50,00 et le montant maximum des deux lignes est de 100,50.
Fonction MDX Year to Date (YTD) et agrégations en cours
MDX est un langage d'expression multidimensionnel utilisé pour interroger des données multidimensionnelles (telles que le cube) et est utilisé dans les solutions d'informatique décisionnelle (BI).
Selon https://docs.microsoft.com/en-us/sql/mdx/ytd-mdx, la fonction Year to Date (YTD) dans MDX fonctionne de la même manière que l'exécution ou le glissement des agrégations. Par exemple, YTD souvent utilisé en combinaison sans paramètre fourni affiche un total cumulé à ce jour.
Cela signifie que si nous appliquons cette fonction sur l'année, cela donne toutes les données de l'année, mais si nous explorons jusqu'en mars, cela nous donnera tous les totaux depuis le début de l'année jusqu'en mars et ainsi de suite.
Ceci est très utile dans les rapports SSRS.
Choses à faire
C'est ça! Vous êtes prêt à effectuer une analyse de données de base après avoir parcouru cet article et vous pouvez améliorer vos compétences en procédant comme suit :
- Veuillez essayer d'écrire un script d'agrégation en cours d'exécution en créant des fenêtres sur d'autres colonnes telles que Montant total.
- Veuillez également essayer d'écrire un script d'agrégats glissants en créant des cadres sur d'autres colonnes telles que Montant total.
- Vous pouvez ajouter plus de colonnes et d'enregistrements au tableau (ou même plus de tableaux) pour essayer d'autres combinaisons d'agrégation.
- Les exemples de scripts mentionnés dans cet article peuvent être transformés en procédures stockées à utiliser dans les rapports SSRS derrière des ensembles de données.
Références :
- Ytd (MDX)
- dbForge Studio pour SQL Server