Interrogation d’un entrepôt de données

Effectué

Lorsque les tables de dimension et de faits d’un entrepôt de données ont été chargées avec des données, vous pouvez utiliser SQL pour interroger les tables et analyser les données qu’elles contiennent. La syntaxe Transact-SQL utilisée pour interroger des tables dans un pool SQL dédié Synapse est similaire à SQL utilisée dans SQL Server ou Azure SQL Database.

Agrégation des mesures par attributs de dimension

La plupart des analyses de données avec un entrepôt de données impliquent l'agrégation des mesures numériques dans les tables de faits par les attributs des tables de dimension. En raison de la façon dont un schéma en étoile ou en flocon est implémenté, les requêtes permettant d’effectuer ce type d’agrégation s’appuient sur JOIN des clauses pour connecter des tables de faits à des tables de dimension et une combinaison de fonctions d’agrégation et GROUP BY de clauses pour définir les hiérarchies d’agrégation.

Par exemple, le code SQL suivant interroge les tables FactSales et DimDate dans un entrepôt de données hypothétique pour agréger les montants des ventes par année et trimestre :

SELECT  dates.CalendarYear,
        dates.CalendarQuarter,
        SUM(sales.SalesAmount) AS TotalSales
FROM dbo.FactSales AS sales
JOIN dbo.DimDate AS dates ON sales.OrderDateKey = dates.DateKey
GROUP BY dates.CalendarYear, dates.CalendarQuarter
ORDER BY dates.CalendarYear, dates.CalendarQuarter;

Les résultats de cette requête ressemblent au tableau suivant :

CalendarYear CalendarQuarter Ventes totales
2020 1 25980.16
2020 2 27453.87
2020 3 28527,15
2020 4 31083.45
2021 1 34562.96
2021 2 36162.27
... ... ...

Vous pouvez joindre autant de tables de dimension que nécessaire pour calculer les agrégations dont vous avez besoin. Par exemple, le code suivant étend l’exemple précédent pour décomposer les totaux des ventes trimestrielles par ville en fonction des détails de l’adresse du client dans la table DimCustomer :

SELECT  dates.CalendarYear,
        dates.CalendarQuarter,
        custs.City,
        SUM(sales.SalesAmount) AS TotalSales
FROM dbo.FactSales AS sales
JOIN dbo.DimDate AS dates ON sales.OrderDateKey = dates.DateKey
JOIN dbo.DimCustomer AS custs ON sales.CustomerKey = custs.CustomerKey
GROUP BY dates.CalendarYear, dates.CalendarQuarter, custs.City
ORDER BY dates.CalendarYear, dates.CalendarQuarter, custs.City;

Cette fois, les résultats incluent un total de ventes trimestrielles pour chaque ville :

CalendarYear CalendarQuarter Ville Ventes totales
2020 1 Amsterdam 5982.53
2020 1 Berlin 2826.98
2020 1 Chicago 5372.72
... ... ... ..
2020 2 Amsterdam 7163.93
2020 2 Berlin 8191.12
2020 2 Chicago 2428.72
... ... ... ..
2020 3 Amsterdam 7261.92
2020 3 Berlin 4202.65
2020 3 Chicago 2287.87
... ... ... ..
2020 4 Amsterdam 8262.73
2020 4 Berlin 5373.61
2020 4 Chicago 7726.23
... ... ... ..
2021 1 Amsterdam 7261.28
2021 1 Berlin 3648.28
2021 1 Chicago 1027.27
... ... ... ..

Jointure dans un schéma en flocon

Dans un schéma en flocon, les dimensions peuvent être partiellement normalisée. Plusieurs jointures sont parfois nécessaires pour lier des tables de faits aux dimensions en flocon. Par exemple, supposons que votre entrepôt de données inclut une table de dimension DimProduct à partir de laquelle les catégories de produits ont été normalisées dans une table DimCategory distincte. Voici un exemple de requête permettant d’agréger des éléments vendus par catégorie de produit :

SELECT  cat.ProductCategory,
        SUM(sales.OrderQuantity) AS ItemsSold
FROM dbo.FactSales AS sales
JOIN dbo.DimProduct AS prod ON sales.ProductKey = prod.ProductKey
JOIN dbo.DimCategory AS cat ON prod.CategoryKey = cat.CategoryKey
GROUP BY cat.ProductCategory
ORDER BY cat.ProductCategory;

Les résultats de cette requête incluent le nombre d’articles vendus pour chaque catégorie de produit :

Catégorie de Produit Articles Vendus
Accessoires 28271
Pièces détachées 5368
... ...

Remarque

Les clauses JOIN pour FactSales et DimProduct et DimProduct et DimCategory sont requises, même si aucun champ de DimProduct n’est retourné par la requête.

Utilisation de fonctions de classement

Un autre type courant de requête analytique consiste à partitionner les résultats en fonction d’un attribut de dimension et à classer les résultats dans chaque partition. Par exemple, vous pouvez établir un classement annuel des magasins en fonction de leur chiffre d’affaires. Pour atteindre cet objectif, vous pouvez utiliser Transact-SQL fonctions de classement telles que ROW_NUMBER, , RANKDENSE_RANK, et NTILE. Elles vous permettent de partitionner les données en catégories, chacune retournant une valeur spécifique qui indique la position relative de chaque ligne dans la partition :

  • ROW_NUMBER retourne la position ordinale de la ligne dans la partition. Par exemple, la première ligne porte le numéro 1, la deuxième le numéro 2, etc.
  • RANK retourne la position classée de chaque ligne dans les résultats ordonnés. Par exemple, dans une partition de magasins triés par volume de ventes, le magasin affichant le volume de ventes le plus élevé obtient le rang 1. Si plusieurs magasins présentent les mêmes volumes de ventes, ils reçoivent le même rang ; le rang attribué aux magasins suivants reflète alors le nombre de magasins dont les volumes de ventes sont plus élevés, égalités comprises.
  • DENSE_RANK classe les lignes d’une partition de la même façon que RANK, à une différence près : lorsque plusieurs lignes possèdent le même rang, le rang des suivantes est établi en ignorant les égalités.
  • NTILE retourne le centile spécifié dans lequel la ligne tombe. Dans une partition de magasins triés par volume de ventes, NTILE(4) retourne le quartile dans lequel le volume de ventes d’un magasin le place.

Par exemple, considérez la requête suivante :

SELECT  ProductCategory,
        ProductName,
        ListPrice,
        ROW_NUMBER() OVER
            (PARTITION BY ProductCategory ORDER BY ListPrice DESC) AS RowNumber,
        RANK() OVER
            (PARTITION BY ProductCategory ORDER BY ListPrice DESC) AS Rank,
        DENSE_RANK() OVER
            (PARTITION BY ProductCategory ORDER BY ListPrice DESC) AS DenseRank,
        NTILE(4) OVER
            (PARTITION BY ProductCategory ORDER BY ListPrice DESC) AS Quartile
FROM dbo.DimProduct
ORDER BY ProductCategory;

La requête partitionne les produits en groupes en fonction de leur catégorie. Dans chaque partition de catégorie, la position relative de chaque produit est déterminée en fonction de son prix catalogue. Voici les résultats possibles de cette requête :

Catégorie de Produit ProductName ListPrice RowNumber Classement DenseRank Quartile
Accessoires Widget 8.99 1 1 1 1
Accessoires Knicknak 8,49 2 2 2 1
Accessoires Sprocket 5,99 3 3 3 2
Accessoires Doodah 5,99 4 3 3 2
Accessoires Spangle 2.99 5 5 4 3
Accessoires Badabing 0.25 6 6 5 4
Pièces détachées Flimflam 7.49 1 1 1 1
Pièces détachées Snickity wotsit 6,99 2 2 2 1
Pièces détachées Flange 4,25 3 3 3 2
... ... ... ... ... ... ...

Remarque

Les résultats de l’exemple illustrent la différence entre RANK et DENSE_RANK. Notez que dans la catégorie Accessoires , les produits Sprocket et Doodah ont le même prix de liste ; et sont tous les deux classés comme le 3ème produit le plus cher. Le produit le plus cher suivant a un RANG de 5 (il y a quatre produits plus chers que celui-ci) et une DENSE_RANK de 4 (il y a trois prix plus élevés).

Pour en savoir plus sur les fonctions de classement, consultez Fonctions de classement (Transact-SQL) dans la documentation Azure Synapse Analytics.

Récupération d’un nombre approximatif

Bien que l’objectif d’un entrepôt de données consiste principalement à prendre en charge les modèles et rapports de données analytiques pour l’entreprise, les analystes et scientifiques des données doivent souvent effectuer une exploration initiale des données, juste pour déterminer l’échelle et la distribution de base des données.

Par exemple, la requête suivante utilise la fonction COUNT pour récupérer le nombre de ventes de chaque année dans un entrepôt de données hypothétique :

SELECT dates.CalendarYear AS CalendarYear,
    COUNT(DISTINCT sales.OrderNumber) AS Orders
FROM FactSales AS sales
JOIN DimDate AS dates ON sales.OrderDateKey = dates.DateKey
GROUP BY dates.CalendarYear
ORDER BY CalendarYear;

Voici les résultats possibles de cette requête :

CalendarYear Ordres
2019 239870
2020 284741
2021 309272
... ...

En raison du volume de données présentes dans un entrepôt de données, même les requêtes simples visant à compter le nombre d’enregistrements qui répondent à des critères spécifiés peuvent prendre beaucoup de temps. Dans de nombreux cas, un nombre précis n’est pas nécessaire : une estimation approximative suffit. Vous pouvez alors utiliser la fonction APPROX_COUNT_DISTINCT, comme dans l’exemple suivant :

SELECT dates.CalendarYear AS CalendarYear,
    APPROX_COUNT_DISTINCT(sales.OrderNumber) AS ApproxOrders
FROM FactSales AS sales
JOIN DimDate AS dates ON sales.OrderDateKey = dates.DateKey
GROUP BY dates.CalendarYear
ORDER BY CalendarYear;

La APPROX_COUNT_DISTINCT fonction utilise un algorithme HyperLogLog pour récupérer un nombre approximatif. Le résultat est garanti présenter un taux d’erreur maximal de 2 % avec une probabilité de 97 %. Voici donc les résultats possibles de cette requête avec les mêmes données hypothétiques qu’auparavant :

CalendarYear ApproxOrders
2019 235552
2020 290436
2021 304633
... ...

Les nombres sont moins précis, mais toujours suffisants pour une comparaison approximative des ventes annuelles. Avec un grand volume de données, la requête utilisant la fonction APPROX_COUNT_DISTINCT est exécutée plus rapidement. La précision réduite peut représenter un compromis acceptable pendant l’exploration des données de base.

Remarque

Pour plus d’informations, consultez la documentation de la fonction APPROX_COUNT_DISTINCT .