La recherche de «fonctions de fenĂȘtre dans Clickhouse» ne renvoie pas de rĂ©sultats significatifs. Cet article est une tentative de rĂ©sumer des donnĂ©es dispersĂ©es sur Internet, des exemples avec ClickHouseMeetup et ma propre expĂ©rience.
Fonctions de fenĂȘtre - syntaxe
Permettez-moi de vous rappeler la syntaxe des fonctions de fenĂȘtre et le type de rĂ©sultat que nous obtenons. Dans les exemples, nous utiliserons le dialecte Standart SQL Google BigQuery. Voici un lien vers la documentation sur les fonctions de fenĂȘtre (elles sont appelĂ©es fonction analytique dans la documentation - des sons de traduction plus prĂ©cis comme des fonctions analytiques). Et voici la liste des fonctions elle-mĂȘme.
La syntaxe générale ressemble à ceci:
analytic_function_name ( [ argument_list ] ) OVER over_clause
over_clause:
{ named_window | ( [ window_specification ] ) }
window_specification:
[ named_window ]
[ PARTITION BY partition_expression [, ...] ]
[ ORDER BY expression [ { ASC | DESC } ] [, ...] ]
[ window_frame_clause ]
window_frame_clause:
{ rows_range } { frame_start | frame_between }
rows_range:
{ ROWS | RANGE }
Prenons les choses Ă©tape par Ă©tape:
- La fonction window est appliquée au jeu d'enregistrements défini dans l'expression over_clause,
- Le jeu d'enregistrements est dĂ©fini par la clause PARTITION BY. Ici, vous pouvez lister un ou plusieurs champs par lesquels le jeu d'enregistrements sera dĂ©terminĂ©. Fonctionne de la mĂȘme maniĂšre que GROUP BY.
Le tri des enregistrements dans un ensemble est spĂ©cifiĂ© Ă l'aide de ORDER BY. - Vous pouvez en outre restreindre un ensemble prĂ©dĂ©fini d'enregistrements en tant que fenĂȘtre. La fenĂȘtre peut ĂȘtre dĂ©finie de maniĂšre statique. Par exemple, vous pouvez prendre 5 enregistrements comme fenĂȘtre, 2 avant et 2 aprĂšs l'enregistrement actuel et l'enregistrement actuel lui-mĂȘme. Cela ressemblera Ă ceci: RANGĂES ENTRE 2 PRĂCĂDENT ET 2 SUIVANTES.
Un exemple de construction pour spĂ©cifier une fenĂȘtre dĂ©finie dynamiquement ressemble Ă ceci - RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Cette construction dĂ©finit une fenĂȘtre du premier Ă l'enregistrement en cours selon l'ordre de tri spĂ©cifiĂ©.
A titre d'exemple, considérons le calcul de la somme cumulée (exemple de la documentation):
SELECT item, purchases, category, SUM(purchases)
OVER (
PARTITION BY category
ORDER BY purchases
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS total_purchases
FROM Produce
RĂ©sultat:
+-------------------------------------------------------+
| item | purchases | category | total_purchases |
+-------------------------------------------------------+
| orange | 2 | fruit | 2 |
| apple | 8 | fruit | 10 |
| leek | 2 | vegetable | 2 |
| cabbage | 9 | vegetable | 11 |
| lettuce | 10 | vegetable | 21 |
| kale | 23 | vegetable | 44 |
+-------------------------------------------------------+
Que peut-on faire dans Clickhouse
Essayons de rĂ©pĂ©ter cet exemple dans ClickHouse. Bien sĂ»r, ClickHouse a les fonctions runningAccumulate , arrayCumSum et groupArrayMovingSum . Mais dans le premier cas, vous devez dĂ©terminer l'Ă©tat dans une sous-requĂȘte ( plus de dĂ©tails ), et dans le second cas, la fonction renvoie un tableau, qui doit ensuite ĂȘtre dĂ©veloppĂ©.
Nous construirons la requĂȘte la plus gĂ©nĂ©rale. La demande elle-mĂȘme pourrait ressembler Ă ceci:
SELECT
items,
summ as purchases,
category,
sumArray(cum_summ) as total_purchases
FROM (SELECT
category,
groupArray(item) AS items,
groupArray(purchases) AS summ,
arrayMap(x -> arraySlice(summ, 1, x), arrayEnumerate(summ)) AS cum_summ
FROM (SELECT
item,
purchases,
category
FROM produce
ORDER BY category, purchases)
GROUP BY category)
ARRAY JOIN items, summ, cum_summ
GROUP BY category, items, summ
ORDER BY category, purchases
Prenons les choses Ă©tape par Ă©tape:
- Tout d'abord, nous construisons une sous-requĂȘte, dans laquelle le tri des donnĂ©es requis a lieu (catĂ©gorie ORDER BY, achats). Le tri doit correspondre aux champs des expressions PARTITION BY et ORDER BY de la fonction de fenĂȘtre.
- , , PARTITION BY. item .
purchases , summ . - â ArrayMap. , func arr.
arr â [1, 2, âŠ, length(summ)], arrayEnumerate.
func â arraySlice(summ, 1, x), x â arr, . summ x. , cum_sum , , .
ArrayMap arrayEnumerate , , . ( 3), ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING.
arrayMap(x -> arraySlice(summ, if(x-1 > 0, x-1, 1), if(x-1 > 0, 3, 2)), arrayEnumerate(summ))
, . 2 ClickHouse:
- â
, , arrayMap arrayFilter.. â ( â ) (alias) arrayMap, arrayFilter . - â . , , arrayReverse arraySlice.
- â
- La derniÚre étape consiste à développer les tableaux dans une table à l'aide de ARRAY JOIN. Nous devons également appliquer la fonction d'agrégation de somme avec le modificateur -Array (en conséquence, la fonction d'agrégation ressemble à sumArray) au résultat renvoyé par la fonction ArrayMap.
Production
Il est possible d'Ă©muler le fonctionnement des fonctions de fenĂȘtre dans ClickHouse. Pas trĂšs rapide et pas trĂšs joli. En bref, le pipeline se compose de 3 Ă©tapes:
- RequĂȘte triĂ©e. Cette Ă©tape prĂ©pare le jeu d'enregistrements.
- Regroupement en baies et exĂ©cution d'opĂ©rations sur les baies. Cette Ă©tape dĂ©finit la fenĂȘtre de notre fonction de fenĂȘtre.
- Expansion dans une table à l'aide de fonctions d'agrégation.