Analyser les données avec les fonctions de fenêtre¶
Dans ce chapitre :
Cette rubrique contient des informations conceptuelles introductives sur les fonctions de fenêtre. Si vous êtes déjà familiarisé avec l’utilisation des fonctions de fenêtre, les informations de référence suivantes peuvent être suffisantes :
Fonctions de fenêtre, qui contient une liste de fonctions et des liens vers des descriptions de fonctions individuelles.
Syntaxe et utilisation des fonctions de fenêtre, qui décrit les règles de syntaxe générales pour toutes les fonctions de fenêtre.
Introduction¶
Une fonction de fenêtre est une fonction SQL analytique qui opère sur un groupe de lignes liées appelées partition. Une partition est généralement un groupe logique de lignes selon une dimension familière, comme la catégorie de produit, l’emplacement, la période ou l’unité commerciale. Les résultats des fonctions sont calculés sur chaque partition, par rapport à un cadre de fenêtre implicite ou explicite. Un cadre de fenêtre est un ensemble fixe ou variable de lignes par rapport à la ligne actuelle. La ligne actuelle est une ligne d’entrée unique pour laquelle le résultat de la fonction est actuellement calculé. Les résultats de la fonction sont calculés ligne par ligne dans chaque partition, et chaque ligne du cadre de la fenêtre prend son tour comme ligne actuelle.
La syntaxe qui définit ce comportement est la clause OVER pour la fonction. Dans de nombreux cas, la clause OVER fait la distinction entre une fonction de fenêtre et une fonction SQL standard avec le même nom (comme AVG ou SUM). La clause OVER se compose de trois éléments principaux :
Une clause PARTITION BY
Une clause ORDER BY
Une spécification de cadre de fenêtre
Selon la fonction ou la requête en question, tous ces composants peuvent être facultatifs ; une fonction de fenêtre avec une clause OVER vide est valide : OVER()
. Cependant, dans la plupart des requêtes analytiques, les fonctions de fenêtre nécessitent un ou plusieurs composants de clause OVER explicites. Vous pouvez appeler une fonction de fenêtre dans n’importe quel contexte prenant en charge d’autres fonctions SQL. Les sections suivantes expliquent plus en détail les concepts derrière les fonctions de fenêtre et présentent quelques exemples d’introduction. Pour des informations complètes sur la syntaxe, voir Syntaxe et utilisation des fonctions de fenêtre.
Fonctions de fenêtre et fonctions d’agrégation¶
Une bonne façon de découvrir les fonctions de fenêtre est de comparer les fonctions d’agrégation classiques avec leurs homologues de fonctions de fenêtre. Plusieurs fonctions d’agrégation standard, comme SUM, COUNT et AVG, ont des fonctions de fenêtre correspondantes avec le même nom. Pour distinguer les deux, notez ce qui suit :
Pour une fonction d’agrégation, l’entrée se compose de plusieurs lignes et la sortie d’une ligne.
Pour une fonction de fenêtre, l’entrée se compose d’une ligne dans une partition et la sortie d’une ligne par ligne d’entrée.
Par exemple, la fonction d’agrégation SUM renvoie une valeur totale unique pour toutes les lignes d’entrée, tandis qu’une fonction de fenêtre renvoie plusieurs totaux : un pour chaque ligne (la ligne actuelle) par rapport à toutes les autres lignes de la partition.
Pour voir comment cela fonctionne, commencez par créer et charger la table menu_items qui contient le coût des marchandises vendues et les prix des articles du menu du foodtruck. Utilisez une fonction AVG standard pour trouver le coût moyen des marchandises pour les articles du menu dans différentes catégories :
SELECT menu_category,
AVG(menu_cogs_usd) avg_cogs
FROM menu_items
GROUP BY 1
ORDER BY menu_category;
+---------------+------------+
| MENU_CATEGORY | AVG_COGS |
|---------------+------------|
| Beverage | 0.60000000 |
| Dessert | 1.79166667 |
| Main | 6.11046512 |
| Snack | 3.10000000 |
+---------------+------------+
Notez que la fonction renvoie un résultat groupé pour avg_cogs
.
Vous pouvez également spécifier une clause OVER et utiliser AVG comme une fonction de fenêtre. (Le résultat est limité à 15 lignes du tableau de 60 lignes.)
SELECT menu_category,
AVG(menu_cogs_usd) OVER(PARTITION BY menu_category) avg_cogs
FROM menu_items
ORDER BY menu_category
LIMIT 15;
+---------------+----------+
| MENU_CATEGORY | AVG_COGS |
|---------------+----------|
| Beverage | 0.60000 |
| Beverage | 0.60000 |
| Beverage | 0.60000 |
| Beverage | 0.60000 |
| Dessert | 1.79166 |
| Dessert | 1.79166 |
| Dessert | 1.79166 |
| Dessert | 1.79166 |
| Dessert | 1.79166 |
| Dessert | 1.79166 |
| Main | 6.11046 |
| Main | 6.11046 |
| Main | 6.11046 |
| Main | 6.11046 |
| Main | 6.11046 |
+---------------+----------+
Notez que la fonction renvoie une moyenne pour chaque ligne de chaque partition et réinitialise le calcul lorsque la valeur de la colonne de partitionnement change. Pour rendre la valeur de la fonction de fenêtre plus apparente, ajoutez une clause ORDER BY et un cadre de fenêtre à la définition de fonction. Renvoyez également les valeurs menu_cogs_usd
brutes, en plus des moyennes. Vous pourrez ainsi voir comment fonctionnent les calculs spécifiques. Cette requête est un exemple simple de « moyenne mobile », un calcul glissant qui dépend d’un cadre de fenêtre explicite. Pour plus d’exemples comme celui-ci, voir Analyse des données de séries temporelles.
SELECT menu_category, menu_price_usd, menu_cogs_usd,
AVG(menu_cogs_usd) OVER(PARTITION BY menu_category ORDER BY menu_price_usd ROWS BETWEEN CURRENT ROW and 2 FOLLOWING) avg_cogs
FROM menu_items
ORDER BY menu_category, menu_price_usd
LIMIT 15;
+---------------+----------------+---------------+----------+
| MENU_CATEGORY | MENU_PRICE_USD | MENU_COGS_USD | AVG_COGS |
|---------------+----------------+---------------+----------|
| Beverage | 2.00 | 0.50 | 0.58333 |
| Beverage | 3.00 | 0.50 | 0.57500 |
| Beverage | 3.00 | 0.75 | 0.63333 |
| Beverage | 3.50 | 0.65 | 0.65000 |
| Dessert | 3.00 | 0.50 | 0.91666 |
| Dessert | 4.00 | 1.00 | 1.58333 |
| Dessert | 5.00 | 1.25 | 2.08333 |
| Dessert | 6.00 | 2.50 | 2.66666 |
| Dessert | 6.00 | 2.50 | 2.75000 |
| Dessert | 7.00 | 3.00 | 3.00000 |
| Main | 5.00 | 1.50 | 2.03333 |
| Main | 6.00 | 2.60 | 3.00000 |
| Main | 6.00 | 2.00 | 2.33333 |
| Main | 6.00 | 2.40 | 3.13333 |
| Main | 8.00 | 4.00 | 3.66666 |
+---------------+----------------+---------------+----------+
Le cadre de fenêtre ajuste les calculs moyens de telle sorte que seules la ligne actuelle et les deux lignes qui la suivent (dans la partition) soient prises en compte. La dernière ligne d’une partition n’a pas de lignes suivantes, donc la moyenne de la dernière ligne Beverage
, par exemple, est la même que la valeur menu_cogs_usd
correspondante (0.65
). La sortie de la fonction de fenêtre dépend de la ligne individuelle transmise à la fonction et des valeurs des autres lignes éligibles pour le cadre de fenêtre.
Ordre des lignes pour les fonctions de fenêtre¶
L’exemple de fonction de fenêtre AVG précédent utilise une clause ORDER BY dans la définition de la fonction pour garantir que le cadre de fenêtre est soumis à des données triées (par menu_price_usd
dans ce cas).
Deux types de fonctions de fenêtre nécessitent une clause ORDER BY :
Fonctions de fenêtre avec cadres de fenêtre explicites qui effectuent des opérations de glissement sur des sous-ensembles de lignes de chaque partition, comme le calcul de totaux continus ou de moyennes mobiles. Sans clause ORDER BY, le cadre de fenêtre n’a pas de sens ; l’ensemble des lignes « précédentes » et « suivantes » doit être déterministe.
Fonctions de fenêtre de classement, comme CUME_DIST, RANK et DENSE_RANK, qui renvoient des informations basées sur le « rang » d’une ligne. Par exemple, si vous classez les magasins par ordre descendant de profit par mois, le magasin qui enregistre le plus de profit sera classé 1 ; le deuxième magasin le plus rentable sera classé 2, etc.
La clause ORDER BY pour une fonction de fenêtre prend en charge la même syntaxe que la clause ORDER BY principale qui trie les résultats finaux d’une requête. Ces deux clauses ORDER BY sont séparées et distinctes. Une clause ORDER BY à l’intérieur d’une clause OVER contrôle uniquement l’ordre dans lequel la fonction de fenêtre traite les lignes ; elle ne contrôle pas la sortie de la requête entière. Dans de nombreux cas, vos requêtes de fonction de fenêtre contiendront les deux types de clauses ORDER BY.
Les clauses PARTITION BY et ORDER BY à l’intérieur de la clause OVER sont également indépendantes. Vous pouvez utiliser la clause ORDER BY sans la clause PARTITION BY, et inversement.
Vérifiez la syntaxe des fonctions de fenêtre individuelles avant d’écrire des requêtes. Les exigences syntaxiques pour la clause ORDER BY varient selon la fonction :
Certaines fonctions de fenêtre nécessitent une clause ORDER BY.
Certaines fonctions de fenêtre utilisent une clause ORDER BY s’il en existe une, mais ne l’exigent pas.
Certaines fonctions de fenêtre n’autorisent pas la clause ORDER BY.
Certaines fonctions de fenêtre interprètent une clause ORDER BY comme un cadre de fenêtre implicite.
Prudence
En général, SQL est un langage explicite, avec peu de clauses implicites. Cependant, pour certaines fonctions de fenêtre, une clause ORDER BY implique un cadre de fenêtre. Pour plus de détails, voir Notes sur l’utilisation des cadres de fenêtre.
Parce qu’un comportement implicite plutôt qu’explicite peut conduire à des résultats difficiles à comprendre, Snowflake recommande de déclarer les cadres de fenêtre de manière explicite.
Utilisation de différents types de cadres de fenêtre¶
Les cadres de fenêtre sont définis explicitement ou implicitement. Ils dépendent de la présence d’une clause ORDER BY à l’intérieur de la clause OVER :
Pour une syntaxe de cadre explicite, voir
windowFrameClause
sous Syntaxe. Vous pouvez définir des limites ouvertes : du début de la partition à la ligne actuelle ; de la ligne actuelle à la fin de la partition ; ou complètement « sans limite » de bout en bout. Vous pouvez également utiliser des décalages explicites (inclus) relatifs à la ligne actuelle de la partition.Les cadres implicites sont utilisés par défaut lorsque la clause OVER n’inclut pas de
windowFrameClause
. Le cadre par défaut dépend de la fonction en question. Voir aussi Notes sur l’utilisation des cadres de fenêtre.
Cadres de fenêtre basés sur des plages ou sur des lignes¶
Snowflake prend en charge deux types de cadres de fenêtre :
- Basé sur des lignes:
Une séquence exacte de lignes appartient au cadre, en fonction d’un décalage physique par rapport à la ligne actuelle. Par exemple,
5 PRECEDING
désigne les cinq lignes précédant la ligne actuelle. Le décalage doit être un nombre. Le mode ROWS est inclusif et est toujours relatif à la ligne courante. Si le nombre spécifié de lignes précédentes ou suivantes dépasse les limites de la partition, Snowflake considère la valeur comme NULL.Si le cadre a des limites ouvertes plutôt que des limites explicitement numérotées, un décalage physique similaire s’applique. Par exemple, ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW signifie que le cadre est constitué de l’ensemble des lignes (zéro ou plus) qui précèdent physiquement la ligne actuelle et de la ligne actuelle elle-même.
- Basé sur des plages:
Une plage logique de lignes appartient au cadre, en fonction d’un décalage par rapport à la valeur ORDER BY pour la ligne actuelle. Par exemple,
5 PRECEDING
correspond aux lignes avec les valeurs ORDER BY qui ont la valeur ORDER BY de la ligne actuelle, plus ou moins un maximum de 5 (plus pour l’ordre DESC, moins pour l’ordre ASC). La valeur de décalage peut être un nombre ou un intervalle.Si le cadre a des limites ouvertes plutôt que numérotées, un décalage logique similaire s’applique. Par exemple, RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW signifie que le cadre se compose de toutes les lignes qui précèdent physiquement la ligne actuelle, de la ligne actuelle elle-même, et de toutes les lignes adjacentes qui ont la même valeur ORDER BY que la ligne actuelle. Pour un cadre de fenêtre RANGE, CURRENT ROW ne signifie pas physiquement la ligne actuelle ; cela signifie toutes les lignes qui ont la même valeur ORDER BY que la ligne physique actuelle.
Les distinctions au sein des cadres de fenêtre ROWS BETWEEN et RANGE BETWEEN sont importantes car les requêtes de fonction de fenêtre peuvent renvoyer des résultats très différents, selon l’expression ORDER BY, les données dans les tables et la définition exacte du cadre. Les exemples suivants illustrent les différences de comportement.
Comparaison de RANGE BETWEEN et de ROWS BETWEEN avec des décalages explicites¶
Un cadre de fenêtre basé sur des plages nécessite une expression ou une colonne ORDER BY et une spécification RANGE BETWEEN. La limite logique du cadre de la fenêtre dépend de la valeur ORDER BY (une constante numérique ou un littéral d’intervalle) pour la ligne actuelle.
Par exemple, une table de séries temporelles intitulée heavy_weather
est définie comme suit :
CREATE OR REPLACE TABLE heavy_weather
(start_time TIMESTAMP, precip NUMBER(3,2), city VARCHAR(20), county VARCHAR(20));
Les lignes d’échantillon dans cette table ressemblent à ceci :
+-------------------------+--------+-------+-------------+
| START_TIME | PRECIP | CITY | COUNTY |
|-------------------------+--------+-------+-------------|
| 2021-12-30 11:23:00.000 | 0.12 | Lebec | Los Angeles |
| 2021-12-30 11:43:00.000 | 0.98 | Lebec | Los Angeles |
| 2021-12-30 13:53:00.000 | 0.23 | Lebec | Los Angeles |
| 2021-12-30 14:53:00.000 | 0.13 | Lebec | Los Angeles |
| 2021-12-30 15:15:00.000 | 0.29 | Lebec | Los Angeles |
| 2021-12-30 17:53:00.000 | 0.10 | Lebec | Los Angeles |
| 2021-12-30 18:53:00.000 | 0.09 | Lebec | Los Angeles |
| 2021-12-30 19:53:00.000 | 0.07 | Lebec | Los Angeles |
| 2021-12-30 20:53:00.000 | 0.07 | Lebec | Los Angeles |
+-------------------------+--------+-------+-------------+
Supposons qu’une requête calcule une moyenne mobile de 3 heures (AVG) dans la colonne precip
(précipitations) à l’aide d’un cadre de fenêtre classé par start_time
:
AVG(precip)
OVER(ORDER BY start_time
RANGE BETWEEN CURRENT ROW AND INTERVAL '3 hours' FOLLOWING)
Étant donné les lignes d’échantillon ci-dessus, lorsque la ligne actuelle est 2021-12-30 11:23:00.000
(la première ligne d’échantillon), seules les deux lignes suivantes tombent à l’intérieur du cadre (2021-12-30 11:43:00.000
et 2021-12-30 13:53:00.000
). Les horodatages ultérieurs sont supérieurs à 3 heures.
Cependant, si vous définissez le cadre de fenêtre sur un intervalle d’un jour, toutes les lignes d’échantillon qui suivent la ligne actuelle se trouvent à l’intérieur du cadre car elles ont toutes des horodatages à la même date (2021-12-30
) :
RANGE BETWEEN CURRENT ROW AND INTERVAL '1 day' FOLLOWING
Si vous deviez modifier cette syntaxe de RANGE BETWEEN à ROWS BETWEEN, le cadre devrait spécifier des limites fixes, qui représentent un nombre exact de lignes : la ligne actuelle plus le nombre exact de lignes classées suivant, comme 1, 3 ou 10 lignes, quelles que soient les valeurs renvoyées par l’expression ORDER BY.
Voir aussi Exemple RANGE BETWEEN avec des décalages numériques explicites.
Comparaison de RANGE BETWEEN et de ROWS BETWEEN avec des limites ouvertes¶
L’exemple suivant compare les résultats lorsque les cadres de fenêtre suivants sont calculés par rapport au même ensemble de lignes :
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
Cet exemple effectue une sélection à partir d’une petite table intitulée menu_items
. Voir Créer et charger la table menu_items.
La fonction de fenêtre SUM agrège les valeurs menu_price_usd
pour chaque partition menu_category
. Avec la syntaxe ROWS BETWEEN, il est facile de voir comment les totaux continus s’additionnent dans chaque partition.
SELECT menu_category, menu_price_usd,
SUM(menu_price_usd)
OVER(PARTITION BY menu_category ORDER BY menu_price_usd
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) sum_price
FROM menu_items
WHERE menu_category IN('Beverage','Dessert','Snack')
ORDER BY menu_category, menu_price_usd;
+---------------+----------------+-----------+
| MENU_CATEGORY | MENU_PRICE_USD | SUM_PRICE |
|---------------+----------------+-----------|
| Beverage | 2.00 | 2.00 |
| Beverage | 3.00 | 5.00 |
| Beverage | 3.00 | 8.00 |
| Beverage | 3.50 | 11.50 |
| Dessert | 3.00 | 3.00 |
| Dessert | 4.00 | 7.00 |
| Dessert | 5.00 | 12.00 |
| Dessert | 6.00 | 18.00 |
| Dessert | 6.00 | 24.00 |
| Dessert | 7.00 | 31.00 |
| Snack | 6.00 | 6.00 |
| Snack | 6.00 | 12.00 |
| Snack | 7.00 | 19.00 |
| Snack | 9.00 | 28.00 |
| Snack | 11.00 | 39.00 |
+---------------+----------------+-----------+
Lorsque la syntaxe RANGE BETWEEN est utilisée avec une requête identique, les calculs ne sont pas si évidents au premier abord ; ils dépendent d’une interprétation différente de la ligne actuelle : la ligne actuelle elle-même plus toutes les lignes adjacentes qui ont la même valeur ORDER BY que cette ligne.
Par exemple, les valeurs sum_price
des deuxième et troisième lignes du résultat sont toutes les deux 8.00
car la valeur ORDER BY de ces lignes est la même. Ce comportement se produit à deux autres endroits dans le jeu de résultats, où sum_price
est calculé consécutivement comme 24.00
et 12.00
.
SELECT menu_category, menu_price_usd,
SUM(menu_price_usd)
OVER(PARTITION BY menu_category ORDER BY menu_price_usd
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) sum_price
FROM menu_items
WHERE menu_category IN('Beverage','Dessert','Snack')
ORDER BY menu_category, menu_price_usd;
+---------------+----------------+-----------+
| MENU_CATEGORY | MENU_PRICE_USD | SUM_PRICE |
|---------------+----------------+-----------|
| Beverage | 2.00 | 2.00 |
| Beverage | 3.00 | 8.00 |
| Beverage | 3.00 | 8.00 |
| Beverage | 3.50 | 11.50 |
| Dessert | 3.00 | 3.00 |
| Dessert | 4.00 | 7.00 |
| Dessert | 5.00 | 12.00 |
| Dessert | 6.00 | 24.00 |
| Dessert | 6.00 | 24.00 |
| Dessert | 7.00 | 31.00 |
| Snack | 6.00 | 12.00 |
| Snack | 6.00 | 12.00 |
| Snack | 7.00 | 19.00 |
| Snack | 9.00 | 28.00 |
| Snack | 11.00 | 39.00 |
+---------------+----------------+-----------+
Cadres de fenêtre pour calculs cumulatifs et glissants¶
Les cadres de fenêtre sont un mécanisme très flexible permettant d’exécuter différents types de requêtes analytiques, y compris les calculs cumulatifs et les calculs mobiles. Pour renvoyer des sommes cumulées, par exemple, vous pouvez spécifier un cadre de fenêtre qui démarre à un point fixe et se déplace ligne par ligne sur toute la partition :
OVER(PARTITION BY col1 ORDER BY col2 ROWS UNBOUNDED PRECEDING)
Un autre exemple de ce type de cadre peut être :
OVER(PARTITION BY col1 ORDER BY col2 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
Le nombre de lignes éligibles à ces cadres est variable, mais les points de début et de fin des cadres sont fixes, utilisant des limites nommées plutôt que des limites numériques ou d’intervalle.
Si vous souhaitez que le calcul de la fonction de fenêtre glisse vers l’avant sur un nombre spécifique (ou une plage) de lignes, vous pouvez utiliser des décalages explicites :
OVER(PARTITION BY col1 ORDER BY col2 ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING)
Dans ce cas, le résultat est un cadre glissant composé d’un maximum de sept lignes (3 + ligne actuelle + 3). Un autre exemple de ce type de cadre peut être :
OVER(PARTITION BY col1 ORDER BY col2 ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING)
Les cadres de fenêtre peuvent contenir un mélange de limites nommées et de décalages explicites.
Cadres de fenêtres glissants¶
Un cadre de fenêtre glissant est un cadre dont la largeur est fixe et qui « glisse le long » des lignes de la partition vous montrant une tranche différente de la partition à chaque fois. Le nombre de lignes dans le cadre reste le même, sauf au début ou à la fin d’une partition, où il peut contenir moins de lignes.
Les fenêtres glissantes sont souvent utilisées pour calculer les moyennes mobiles, qui sont basées sur un intervalle de taille fixe (comme un certain nombre de jours). La moyenne est « mobile » car, bien que la taille de l’intervalle soit constante, les valeurs réelles de cet intervalle changent avec le temps (ou une autre dimension).
Par exemple, les analystes boursiers analysent souvent des actions en se basant en partie sur la moyenne mobile de 13 semaines du cours d’une action. Le prix moyen mobile aujourd’hui correspond à la moyenne des prix à la fin de la journée et du prix à la fin de chaque jour au cours des 13 dernières semaines. Si les actions sont échangées 5 jours par semaine et s’il n’y a pas eu de jours fériés au cours des 13 dernières semaines, la moyenne mobile correspond au cours moyen des 65 derniers jours d’échange (y compris aujourd’hui).
L’exemple suivant montre ce qu’il advient d’une moyenne mobile du cours d’une action sur 13 semaines (91 jours) le dernier jour du mois de juin et les premiers jours du mois de juillet :
Le 30 juin, la fonction renvoie le prix moyen du 1er avril au 30 juin (inclus).
Le 1er juillet, la fonction renvoie le prix moyen du 2 avril au 1er juillet (inclus).
Le 2 juillet, la fonction renvoie le prix moyen du 3 avril au 2 juillet (inclus).
L’exemple suivant utilise une petite fenêtre glissante (trois jours) sur les sept premiers jours du mois. Cet exemple prend en compte le fait qu’au début de la période, la partition peut ne pas être pleine :
![Cadre de fenêtre coulissante de 3 jours dans une fenêtre de 7 jours](../_images/window-functions-sliding-frame.png)
Comme vous pouvez le voir dans la maquette correspondante d’un résultat de requête, la dernière colonne contient la somme des données de vente des trois derniers jours. Par exemple, la valeur de colonne pour le jour 4 est 36
, ce qui correspond à la somme des ventes des jours 2, 3 et 4 (11 + 12 + 13
) :
+--------+-------+---------------+ | Day of | Sales | Most Recent | | Month | Today | 3 Days' Sales | |--------+-------+---------------+ | 1 | 10 | 10 | | 2 | 11 | 21 | | 3 | 12 | 33 | | 4 | 13 | 36 | | 5 | 14 | 39 | | ... | ... | ... | +--------+-------+---------------+
Fonctions de fenêtre de classement¶
La syntaxe d’une fonction de fenêtre de classement est essentiellement la même que celle d’autres fonctions de fenêtre. Les exceptions sont les suivantes :
Les fonctions de fenêtre de classement nécessitent que la clause ORDER BY soit à l’intérieur de la clause OVER.
Pour certaines fonctions de classement, telles que RANK, aucun argument d’entrée n’est requis. Pour la fonction RANK, la valeur renvoyée est basée uniquement sur le classement numérique, tel que déterminé par la clause ORDER BY à l’intérieur de la clause OVER. Par conséquent, il est inutile de transmettre un nom de colonne ou une expression à la fonction.
La fonction de classement la plus simple s’appelle RANK. Vous pouvez utiliser cette fonction pour :
Classer les commerciaux en fonction des chiffres de ventes, du plus élevé au plus bas.
Classer les pays en fonction de leur GDP par habitant (revenu par personne), du plus élevé au plus faible.
Classer les pays en fonction de la pollution atmosphérique, du moins pollué au plus pollué.
Cette fonction identifie simplement la position de classement numérique d’une ligne dans un ensemble classé de lignes. La première ligne a le rang 1, la deuxième le rang 2, et ainsi de suite. L’exemple suivant montre l’ordre de classement des commerciaux en fonction du Amount Sold
:
+-------------+-------------+------+ | Salesperson | Amount Sold | Rank | |-------------+-------------+------| | Smith | 2000 | 1 | | Jones | 1500 | 2 | | Torkelson | 1200 | 3 | | Dolenz | 1100 | 4 | +-------------+-------------+------+
Les lignes doivent être triées avant l’attribution des classements. Par conséquent, vous devez utiliser une clause ORDER BY à l’intérieur de la clause OVER.
Prenons l’exemple suivant : vous souhaitez comparer le bénéfice de votre magasin par rapport à celui des filiales de la chaîne de magasins, c’est-à-dire savoir si votre magasin se classe en premier, en deuxième, en troisième, etc. Cet exemple classe chaque magasin en fonction de la rentabilité de sa ville. Les lignes sont classées par ordre décroissant (bénéfice le plus élevé en premier), ainsi le magasin le plus rentable a le rang 1 :
SELECT city, branch_ID, net_profit, RANK() OVER (PARTITION BY city ORDER BY net_profit DESC) AS rank FROM store_sales ORDER BY city, rank; +-----------+-----------+------------+------+ | CITY | BRANCH_ID | NET_PROFIT | RANK | |-----------+-----------+------------+------| | Montreal | 3 | 10000.00 | 1 | | Montreal | 4 | 9000.00 | 2 | | Vancouver | 2 | 15000.00 | 1 | | Vancouver | 1 | 10000.00 | 2 | +-----------+-----------+------------+------+
Note
La colonne net_profit
n’a pas besoin d’être transférée comme argument à la fonction RANK. Au lieu de cela, les lignes d’entrée sont triées par net_profit
. La fonction RANK doit simplement renvoyer la position de la ligne (1, 2, 3, etc.) dans la partition.
La sortie d’une fonction de classement dépend de :
La ligne individuelle transmise à la fonction.
Des valeurs des autres lignes de la partition.
De l’ordre de toutes les lignes dans la partition.
Snowflake fournit différentes fonctions de classement. Pour une liste de ces fonctions et plus de détails sur leur syntaxe, voir Fonctions de fenêtre.
Pour classer votre magasin par rapport à tous les autres magasins de la chaîne, et pas seulement par rapport aux autres magasins de votre ville, utilisez la requête ci-dessous :
SELECT
branch_ID,
net_profit,
RANK() OVER (ORDER BY net_profit DESC) AS sales_rank
FROM store_sales
La requête suivante utilise la première clause ORDER BY pour contrôler le traitement par la fonction de fenêtre et la seconde clause ORDER BY pour contrôler l’ordre de la sortie de la requête entière :
SELECT
branch_ID,
net_profit,
RANK() OVER (ORDER BY net_profit DESC) AS sales_rank
FROM store_sales
ORDER BY branch_ID;
Exemple illustré¶
Cet exemple utilise un exemple sur les commerciaux pour illustrer plusieurs des concepts décrits précédemment dans cette rubrique.
Supposons que vous souhaitiez générer un rapport financier indiquant des valeurs basées sur les ventes de la dernière semaine :
Ventes quotidiennes
Classement dans la semaine (c’est-à-dire que les ventes ont été classées dans l’ordre croissant pour la semaine)
Ventes réalisées jusqu’à présent cette semaine (c’est-à-dire le « montant cumulé » pour tous les jours depuis le début de la semaine jusqu’à la date du jour)
Total des ventes pour la semaine
Moyenne mobile sur 3 jours (c’est-à-dire la moyenne du jour actuel et des deux jours précédents)
Le rapport pourrait ressembler à ceci :
+--------+-------+------+--------------+-------------+--------------+ | Day of | Sales | Rank | Sales So Far | Total Sales | 3-Day Moving | | Week | Today | | This Week | This Week | Average | |--------+-------+------+--------------+-------------|--------------+ | 1 | 10 | 4 | 10 | 84 | 10.0 | | 2 | 14 | 3 | 24 | 84 | 12.0 | | 3 | 6 | 5 | 30 | 84 | 10.0 | | 4 | 6 | 5 | 36 | 84 | 9.0 | | 5 | 14 | 3 | 50 | 84 | 10.0 | | 6 | 16 | 2 | 66 | 84 | 11.0 | | 7 | 18 | 1 | 84 | 84 | 12.0 | +--------+-------+------+--------------+-------------+--------------+
Le SQL pour cette requête est quelque peu complexe. Plutôt que d’afficher l’exemple sous la forme d’une requête unique, cette discussion décompose le SQL pour les colonnes individuelles.
Dans un scénario réel, vous disposez de plusieurs années de données. Par conséquent, pour calculer des sommes et des moyennes pour une semaine de données spécifique, vous devez utiliser une fenêtre d’une semaine ou un filtre similaire à ceci :
... WHERE date >= start_of_relevant_week and date <= end_of_relevant_week ...
Toutefois, pour cet exemple, nous supposons que la table ne contient que les données de la semaine la plus récente.
CREATE TABLE store_sales_2 ( day INTEGER, sales_today INTEGER ); +-------------------------------------------+ | status | |-------------------------------------------| | Table STORE_SALES_2 successfully created. | +-------------------------------------------+ INSERT INTO store_sales_2 (day, sales_today) VALUES (1, 10), (2, 14), (3, 6), (4, 6), (5, 14), (6, 16), (7, 18); +-------------------------+ | number of rows inserted | |-------------------------| | 7 | +-------------------------+
Calcul du classement des ventes¶
La colonne Rank
est calculée à l’aide de la fonction RANK :
SELECT day, sales_today, RANK() OVER (ORDER BY sales_today DESC) AS Rank FROM store_sales_2 ORDER BY day; +-----+-------------+------+ | DAY | SALES_TODAY | RANK | |-----+-------------+------| | 1 | 10 | 5 | | 2 | 14 | 3 | | 3 | 6 | 6 | | 4 | 6 | 6 | | 5 | 14 | 3 | | 6 | 16 | 2 | | 7 | 18 | 1 | +-----+-------------+------+
Bien qu’il y ait sept jours dans la période, il n’y a que cinq rangs différents (1, 2, 3, 5, 6). Il y avait deux ex aequo (pour la 3e place et la 6e place), il n’y a donc pas de lignes avec les rangs 4 ou 7.
Calcul des ventes réalisées jusqu’à présent cette semaine¶
La colonne Sales So Far This Week
est calculée en utilisant SUM en tant que fonction de fenêtre avec un cadre de fenêtre :
SELECT day, sales_today, SUM(sales_today) OVER (ORDER BY day ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS "SALES SO FAR THIS WEEK" FROM store_sales_2 ORDER BY day; +-----+-------------+------------------------+ | DAY | SALES_TODAY | SALES SO FAR THIS WEEK | |-----+-------------+------------------------| | 1 | 10 | 10 | | 2 | 14 | 24 | | 3 | 6 | 30 | | 4 | 6 | 36 | | 5 | 14 | 50 | | 6 | 16 | 66 | | 7 | 18 | 84 | +-----+-------------+------------------------+
Cela signifie de classer les lignes par date, puis de calculer pour chaque date la somme des ventes réalisées à partir du début du fenêtrage jusqu’à la date actuelle (comprise).
Calcul du total des ventes cette semaine¶
La colonne Total Sales This Week
est calculée à l’aide de la fonction SUM :
SELECT day,
sales_today,
SUM(sales_today)
OVER ()
AS total_sales
FROM store_sales_2
ORDER BY day;
+-----+-------------+-------------+
| DAY | SALES_TODAY | TOTAL_SALES |
|-----+-------------+-------------|
| 1 | 10 | 84 |
| 2 | 14 | 84 |
| 3 | 6 | 84 |
| 4 | 6 | 84 |
| 5 | 14 | 84 |
| 6 | 16 | 84 |
| 7 | 18 | 84 |
+-----+-------------+-------------+
Calcul d’une moyenne mobile sur trois jours¶
La colonne 3-Day Moving Average
est calculée en utilisant AVG en tant que fonction de fenêtre avec un cadre de fenêtre :
SELECT day,
sales_today,
AVG(sales_today)
OVER (ORDER BY day ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
AS "3-DAY MOVING AVERAGE"
FROM store_sales_2
ORDER BY day;
+-----+-------------+----------------------+
| DAY | SALES_TODAY | 3-DAY MOVING AVERAGE |
|-----+-------------+----------------------|
| 1 | 10 | 10.000 |
| 2 | 14 | 12.000 |
| 3 | 6 | 10.000 |
| 4 | 6 | 8.666 |
| 5 | 14 | 8.666 |
| 6 | 16 | 12.000 |
| 7 | 18 | 16.000 |
+-----+-------------+----------------------+
La différence entre ce cadre de fenêtre et le cadre de fenêtre décrit précédemment constitue le point de départ : une limite fixe par rapport à un décalage explicite.
Mettre tous ensemble¶
Voici la version finale de la requête. Elle indique toutes les colonnes :
SELECT day,
sales_today,
RANK()
OVER (ORDER BY sales_today DESC) AS Rank,
SUM(sales_today)
OVER (ORDER BY day
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS "SALES SO FAR THIS WEEK",
SUM(sales_today)
OVER ()
AS total_sales,
AVG(sales_today)
OVER (ORDER BY day ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
AS "3-DAY MOVING AVERAGE"
FROM store_sales_2
ORDER BY day;
+-----+-------------+------+------------------------+-------------+----------------------+
| DAY | SALES_TODAY | RANK | SALES SO FAR THIS WEEK | TOTAL_SALES | 3-DAY MOVING AVERAGE |
|-----+-------------+------+------------------------+-------------+----------------------|
| 1 | 10 | 5 | 10 | 84 | 10.000 |
| 2 | 14 | 3 | 24 | 84 | 12.000 |
| 3 | 6 | 6 | 30 | 84 | 10.000 |
| 4 | 6 | 6 | 36 | 84 | 8.666 |
| 5 | 14 | 3 | 50 | 84 | 8.666 |
| 6 | 16 | 2 | 66 | 84 | 12.000 |
| 7 | 18 | 1 | 84 | 84 | 16.000 |
+-----+-------------+------+------------------------+-------------+----------------------+
Exemples supplémentaires¶
Cette section fournit plus d’exemples de fonctions de fenêtre et illustre comment les clauses PARTITION BY et ORDER BY fonctionnent ensemble.
Ces exemples utilisent le tableau et les données suivants :
CREATE TABLE sales (sales_date DATE, quantity INTEGER);
INSERT INTO sales (sales_date, quantity) VALUES
('2018-01-01', 1),
('2018-01-02', 3),
('2018-01-03', 5),
('2018-02-01', 2)
;
Fonction fenêtre avec clause ORDER BY¶
La clause ORDERBY contrôle l’ordre des données dans chaque fenêtre (et chaque partition s’il y a plus d’une partition). Ceci est utile si vous souhaitez afficher une « somme en cours » sur une période donnée, à mesure que de nouvelles lignes sont ajoutées.
Une somme en cours peut être calculée soit au début du fenêtrage jusqu’à la ligne actuelle (comprise) soit à partir de la ligne actuelle jusqu’à la fin du fenêtrage.
Une requête peut utiliser une fenêtre « glissante », qui est une fenêtre de largeur fixe qui traite n lignes spécifiées par rapport à la ligne actuelle (par exemple les 10 lignes les plus récentes, y compris la ligne actuelle).
Cadres de fenêtre à limites fixes¶
Dans un cadre de fenêtre à limites fixes, les valeurs peuvent être calculées à partir du début du fenêtrage jusqu’à la ligne actuelle (ou à partir de la ligne actuelle jusqu’à la fin de la fenêtre) :
SELECT MONTH(sales_date) AS MONTH_NUM,
quantity,
SUM(quantity) OVER (ORDER BY MONTH(sales_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS CUMULATIVE_SUM_QUANTITY
FROM sales
ORDER BY sales_date;
Le résultat de la requête inclut des commentaires supplémentaires indiquant comment la colonne CUMULATIVE_SUM_QUANTITY
a été calculée :
+-----------+----------+-------------------------+
| MONTH_NUM | QUANTITY | CUMULATIVE_SUM_QUANTITY |
|-----------+----------+-------------------------|
| 1 | 1 | 1 | -- sum = 1
| 1 | 3 | 4 | -- sum = 1 + 3
| 1 | 5 | 9 | -- sum = 1 + 3 + 5
| 2 | 2 | 11 | -- sum = 1 + 3 + 5 + 2
+-----------+----------+-------------------------+
Cadres de fenêtre avec décalages explicites¶
Dans le monde financier, les analystes étudient souvent les « moyennes mobiles ».
Par exemple, vous pourriez voir un graphique dans lequel l’axe des X est le temps et l’axe des Y indique le cours moyen des actions au cours des 13 dernières semaines (c.-à-d. une moyenne mobile sur 13 semaines). Dans un graphique représentant une moyenne mobile du cours d’une action sur 13 semaines, le cours indiqué au 30 juin ne correspond pas au cours de l’action au 30 juin, mais au cours moyen de l’action pour les 13 semaines allant jusqu’au 30 juin inclus (du 1er avril au 30 juin). La valeur au 1er juillet est le cours moyen du 2 avril au 1er juillet ; la valeur du 2 juillet correspond au cours moyen du 3 avril au 2 juillet, et ainsi de suite. Chaque jour, la fenêtre ajoute effectivement la valeur du jour le plus récent à la moyenne mobile et supprime la valeur du jour le plus ancien. Cela lisse les fluctuations au jour le jour et peut rendre les tendances plus faciles à reconnaître.
Les moyennes mobiles peuvent être calculées en utilisant un cadre de fenêtre glissant. Le cadre a une largeur spécifique en lignes. Dans l’exemple ci-dessus, 13 semaines correspondent à 91 jours. Le fenêtrage glissant serait donc de 91 jours. Si les mesures sont prises une fois par jour (par exemple, à la fin de la journée), la fenêtre aura 91 lignes « de large ».
Pour définir une fenêtre large de 91 lignes :
SELECT AVG(price) OVER(ORDER BY timestamp1 ROWS BETWEEN 90 PRECEDING AND CURRENT ROW)
FROM sales;
Note
Le cadre de fenêtre initial peut être inférieur à 91 jours. Par exemple, supposons que vous souhaitiez obtenir le prix moyen mobile d’une action sur 13 semaines. Si le stock a été créé pour la première fois le 1er avril, puis le 3 avril, il n’y a que 3 jours d’informations sur les prix. La fenêtre s’étend donc sur trois lignes.
L’exemple suivant montre le résultat de la somme sur un cadre de fenêtre glissant suffisamment large pour contenir deux échantillons :
SELECT MONTH(sales_date) AS MONTH_NUM,
quantity,
SUM(quantity) OVER (ORDER BY sales_date
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
AS SLIDING_SUM_QUANTITY
FROM sales
ORDER BY sales_date;
Le résultat de la requête inclut des commentaires supplémentaires indiquant comment la colonne SLIDING_SUM_QUANTITY
a été calculée :
+-----------+----------+----------------------+
| MONTH_NUM | QUANTITY | SLIDING_SUM_QUANTITY |
|-----------+----------+----------------------+
| 1 | 1 | 1 | -- sum = 1
| 1 | 3 | 4 | -- sum = 1 + 3
| 1 | 5 | 8 | -- sum = 3 + 5 (1 is no longer in the window)
| 2 | 2 | 7 | -- sum = 5 + 2 (3 is no longer in the window)
+-----------+----------+----------------------+
Notez que la fonctionnalité « fenêtre glissante » nécessite la clause ORDER BY ; la fonction dépend de l’ordre des lignes qui entrent et sortent du cadre de fenêtre.
Totaux continus avec clauses PARTITION BY et ORDER BY¶
Vous pouvez combiner les clauses PARTITION BY et ORDER BY pour obtenir des sommes en cours dans les partitions. Dans cet exemple, les partitions durent un mois et, comme les sommes s’appliquent uniquement à l’intérieur d’une partition, la somme est réinitialisée à 0
au début de chaque nouveau mois :
SELECT MONTH(sales_date) AS MONTH_NUM,
SUM(quantity) OVER (PARTITION BY MONTH(sales_date) ORDER BY sales_date)
AS MONTHLY_CUMULATIVE_SUM_QUANTITY
FROM sales
ORDER BY sales_date;
Le résultat de la requête inclut des commentaires supplémentaires indiquant comment la colonne MONTHLY_CUMULATIVE_SUM_QUANTITY
a été calculée :
+-----------+---------------------------------+
| MONTH_NUM | MONTHLY_CUMULATIVE_SUM_QUANTITY |
|-----------+---------------------------------+
| 1 | 1 | -- sum = 1
| 1 | 4 | -- sum = 1 + 3
| 1 | 9 | -- sum = 1 + 3 + 5
| 2 | 2 | -- sum = 0 + 2 (new month)
+-----------+---------------------------------+
Vous pouvez combiner des partitions et des cadres de fenêtre glissants. Dans l’exemple ci-dessous, la fenêtre glissante est généralement large de deux lignes, mais chaque fois qu’une nouvelle partition (c’est-à-dire, un nouveau mois) est atteinte, la fenêtre glissante commence uniquement par la première ligne de cette partition :
SELECT
MONTH(sales_date) AS MONTH_NUM,
quantity,
SUM(quantity) OVER (PARTITION BY MONTH(sales_date)
ORDER BY sales_date
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
AS MONTHLY_SLIDING_SUM_QUANTITY
FROM sales
ORDER BY sales_date;
Le résultat de la requête inclut des commentaires supplémentaires indiquant comment la colonne MONTHLY_SLIDING_SUM_QUANTITY
a été calculée :
+-----------+----------+------------------------------+
| MONTH_NUM | QUANTITY | MONTHLY_SLIDING_SUM_QUANTITY |
|-----------+----------+------------------------------+
| 1 | 1 | 1 | -- sum = 1
| 1 | 3 | 4 | -- sum = 1 + 3
| 1 | 5 | 8 | -- sum = 3 + 5
| 2 | 2 | 2 | -- sum = 0 + 2 (new month)
+-----------+----------+------------------------------+
Calcul du rapport d’une valeur à une somme de valeurs¶
Vous pouvez utiliser la fonction RATIO_TO_REPORT pour calculer le rapport d’une valeur à la somme des valeurs d’une partition, puis renvoyer le rapport sous forme de pourcentage de cette somme. La fonction divise la valeur de la ligne actuelle par la somme des valeurs de toutes les lignes d’une partition.
SELECT branch_ID,
city,
100 * RATIO_TO_REPORT(net_profit) OVER (PARTITION BY city)
FROM store_sales AS s1
ORDER BY city, branch_ID;
+-----------+-----------+------------------------------------------------------------+
| BRANCH_ID | CITY | 100 * RATIO_TO_REPORT(NET_PROFIT) OVER (PARTITION BY CITY) |
|-----------+-----------+------------------------------------------------------------|
| 3 | Montreal | 52.63157900 |
| 4 | Montreal | 47.36842100 |
| 1 | Vancouver | 40.00000000 |
| 2 | Vancouver | 60.00000000 |
+-----------+-----------+------------------------------------------------------------+
La clause PARTITION BY définit les partitions dans la colonne city
. Si vous voulez voir le pourcentage de bénéfice par rapport à la chaîne entière, et pas seulement par rapport aux magasins d’une ville spécifique, omettez la clause PARTITION BY :
SELECT branch_ID,
100 * RATIO_TO_REPORT(net_profit) OVER ()
FROM store_sales AS s1
ORDER BY branch_ID;
+-----------+-------------------------------------------+
| BRANCH_ID | 100 * RATIO_TO_REPORT(NET_PROFIT) OVER () |
|-----------+-------------------------------------------|
| 1 | 22.72727300 |
| 2 | 34.09090900 |
| 3 | 22.72727300 |
| 4 | 20.45454500 |
+-----------+-------------------------------------------+