Fonctions lambda en SQL ... réfléchissons

image

De quoi parlera l'article, et donc son nom l'indique.

De plus, l'auteur expliquera pourquoi cela est nécessaire de son point de vue, et expliquera que SUBJ n'est pas seulement une technologie à la mode, mais aussi «une entreprise doublement nécessaire - à la fois agréable et utile».

Il est toujours intĂ©ressant de voir comment plusieurs personnes talentueuses font quelque chose (un langage de programmation, pourquoi pas), sachant exactement quel problĂšme elles rĂ©solvent et quelles tĂąches elles se fixent. Et aussi tester leur crĂ©ation sur eux-mĂȘmes. À ne pas comparer avec les crĂ©ations monumentales des comitĂ©s gĂ©ants, qui mettent au premier plan le maintien de l'harmonie de l'univers, qui sait comment.

Comparez, par exemple, le sort de FORTRAN et de PL / 1 . Qui se souviendra maintenant de ce PL / 1.

De ce point de vue, AWK , par exemple, est trĂšs rĂ©ussi. Il vaut la peine de dire qu'en son nom A est Alfred Aho , l'un des auteurs de Dragon Book , W est Peter Weinberger , qui avait un coup de main Ă  Fortran-77, K est Brian Kernigan , oĂč serait-il sans lui. Le langage est destinĂ© au traitement de flux de texte Ă  la volĂ©e dans des canaux entre les processus.

Le langage est sans type ( ce n'est pas tout à fait vrai ), sa syntaxe est trÚs similaire à C, il a des capacités de filtrage, des tableaux associatifs, des événements de début / fin de flux, un événement de nouvelle ligne ...

L'auteur a toujours Ă©tĂ© impressionnĂ© par cette langue Ă©galement par le fait que son interprĂšte n'a pas besoin d'ĂȘtre installĂ©, sous les systĂšmes de type UNIX, il est toujours lĂ , et sous Windows, il suffit de copier le fichier exĂ©cutable et tout fonctionne. Mais ce n'est pas le cas.

Dans le processus, l'auteur doit utiliser le bundle SQL + AWK assez souvent, et c'est pourquoi. SQL est toujours un langage initialement dĂ©claratif conçu pour contrĂŽler les flux de donnĂ©es. Il offre des possibilitĂ©s trĂšs limitĂ©es de travailler avec le contexte d'exĂ©cution de requĂȘte sous la forme de fonctions d'agrĂ©gation.

Comment, par exemple, construire un histogramme bidimensionnel en utilisant SQL?

--   100 x 100 SELECT count(), round(x, -2) AS cx, round(y, -2) AS cy FROM samples GROUP BY cx, xy 

Mais disons que l'utilisation de GROUP BY implique le tri, et ce n'est pas un plaisir bon marché si vous avez des centaines de millions (voire plus) de lignes.
UPD: dans les commentaires, ils m'ont corrigé que ce n'est pas entiÚrement vrai (ou pas du tout)
Le processeur SQL a la capacité d'exécuter des fonctions d'agrégation dans le processus de construction d'un hachage selon le critÚre de regroupement. Pour cela, il est nécessaire qu'il possÚde la quantité de mémoire libre suffisante pour placer la carte de hachage en mémoire.

Ensuite, les contextes des groupes seront mis à jour à mesure que le tableau est lu et à la fin de cette lecture, nous aurons déjà le résultat calculé.
La mĂȘme technique peut ĂȘtre Ă©tendue aux fonctions de fenĂȘtre (ci-dessous), seul le contexte sera «plus Ă©pais».

Dans le cas oĂč le nombre de groupes est inconnu Ă  l'avance ou trĂšs important, le processeur SQL est obligĂ© de crĂ©er un index temporaire et de le parcourir lors d'un deuxiĂšme passage.

Dans des cas simples, par exemple, comme ici - un simple COUNT, une option universelle est possible - un index temporaire (cx, cy, count), puis avec un petit nombre de groupes, tout sera en mĂ©moire sur les pages en cache. Dans les cas complexes, les fonctions de fenĂȘtre, l'Ă©tat du groupe devient non trivial et constamment (dĂ©) sĂ©rialisant ce n'est pas du tout ce que le mĂ©decin a ordonnĂ©.
Résumé: Le processeur SQL a recours au tri lorsqu'il ne peut pas estimer le nombre de groupes aprÚs GROUP BY. Cependant, le regroupement par valeurs calculées est (souvent) juste le cas.

Par conséquent, vous devez faire quelque chose comme:

 psql -t -q -c 'select x, y from samples' | gawk -f mk_hist2d.awk 

oĂč mk_hist2d.awk accumule des statistiques dans le tableau associatif et les affiche Ă  la fin du travail

 # mk_hist2d.awk { bucket[int($2*0.01), int($3*0.01)]+=$1; } END { for (i=0; i < 500; i++) for (j=0; j < 500; j++) { if ((i, j) in bucket) print i*100." "j*100." "bucket[i, j]; else print i*100." "j*100." 0"; } } 

Il y a un MAIS - le flux de donnĂ©es complet doit ĂȘtre envoyĂ© du serveur Ă  la machine qui fonctionne, et ce n'est pas si bon marchĂ©.

Est-il possible de combiner en quelque sorte l'agrĂ©able avec l'utile - d'accumuler des statistiques lors de l'exĂ©cution de la requĂȘte SQL, mais sans recourir au tri? Oui, par exemple, en utilisant des fonctions d'agrĂ©gation personnalisĂ©es.

Fonctions d'agrégation personnalisées


Subj est présent dans différents systÚmes, partout il se fait un peu à sa maniÚre.

  1. PostgreSQL La documentation est ici . Plus de détails ici .
    C'est là que le solde maximum du compte est calculé.
    Et ceci est un exemple qui calcule ce qui est le plus dans la colonne booléenne - vrai ou faux.

    Cela ressemble Ă  ceci -

     CREATE AGGREGATE mode(boolean) ( SFUNC = mode_bool_state, STYPE = INT[], FINALFUNC = mode_bool_final, INITCOND = '{0,0}' ); 

    Ici SFUNC est une fonction qui est appelée pour chaque ligne du flux,
    le premier argument est de type STYPE .

    FINALFUNC est utilisé pour finaliser les calculs et renvoie la valeur de l'agrégat.
    INITCOND - initialisation de la valeur initiale de l'état interne ( STYPE ), passée comme premier argument.
    Étant donnĂ© que les fonctions peuvent ĂȘtre Ă©crites en C (ce qui signifie que pour l'Ă©tat interne, vous pouvez utiliser la mĂ©moire qui est automatiquement libĂ©rĂ©e lorsque vous fermez la demande), c'est un outil trĂšs puissant. Hors du cadre de son utilisation, il faut encore pouvoir y aller.
  2. MS SQL
    Auparavant (2000), avant la requĂȘte, il Ă©tait nĂ©cessaire de crĂ©er un objet ActiveX, pour faire une agrĂ©gation en utilisant cet objet.
    Maintenant (2016+), cela se fait dans l'environnement CLR. Vous devrez créer une fonction personnalisée, créer et enregistrer un assemblage . Ensuite, vous pouvez créer un agrégat .
    Un exemple de calcul de la moyenne géométrique, ainsi que de fusion de chaßnes: avec des paramÚtres supplémentaires et un type défini par l'utilisateur pour stocker un état intermédiaire.
  3. Oracle
    Dans Oracle, cela se fait à l'aide de la cartouche de données ODCIAggregate (interface).
    Pour créer votre propre agrégat, vous devez écrire un type personnalisé qui implémente 4 méthodes
    - l'initialisation (ODCIAggregateInitialize), statique, doit créer une instance du type souhaité et retourner via le paramÚtre
    - itérations (ODCIAggregateIterate), appelées sur chaque ligne de données
    - merge (ODCIAggregateMerge), utilisé pour fusionner des agrégats exécutés en parallÚle
    - finition (ODCIAggregateTerminate) - sortie du résultat
    Exemples: 1 , 2 , 3 , 4 .
  4. DB2
    Il n'existe aucun moyen explicite d'utiliser des agrégats personnalisés dans DB2.
    Mais vous pouvez glisser une fonction standard (bien que MAX) dans un type dĂ©fini par l'utilisateur (en Java) et obliger le systĂšme Ă  exĂ©cuter des requĂȘtes de la forme

     CREATE TYPE Complex AS ( real DOUBLE, i DOUBLE ) 
 CREATE TABLE complexNumbers ( id INTEGER NOT NULL PRIMARY KEY, number Complex ) 
 SELECT sum..real, sum..i FROM ( SELECT GetAggrResult(MAX(BuildComplexSum(number))) FROM complexNumbers ) AS t(sum) 

Qu'est-ce qui est remarquable dans tous ces systĂšmes?

  • D'une maniĂšre ou d'une autre, vous devrez crĂ©er des objets dans la base de donnĂ©es. Que ce soit AGRÉGÉ ou TYPE. Au minimum, des droits appropriĂ©s sont requis. Et je veux juste ajouter quelques chiffres sur son genou.
  • Vous devrez peut-ĂȘtre Ă©crire quelque chose dans un autre langage, que ce soit C, C # ou Java.
    Pour intégrer ce qui est écrit dans le systÚme, encore une fois, des droits sont nécessaires. Mais tout ce que je veux ...
  • DifficultĂ© Ă  s'initialiser. Supposons que vous souhaitiez lire des histogrammes avec diffĂ©rentes tailles de panier. Il semblerait que ce soit plus facile - nous indiquerons l'INITCOND souhaitĂ© lors de la dĂ©claration de l'agrĂ©gat (PostgreSQL) et de l'ensemble de l'entreprise. Mais alors, pour chaque taille du panier, vous aurez besoin de votre propre agrĂ©gat, et pour cela, encore une fois, les droits sont nĂ©cessaires.

    Ici, vous pouvez recourir à une sale astuce et faire glisser le processeur d'union de la ligne d'initialisation (vers l'avant) et des données, construire le contexte non pas dans le constructeur, mais lorsque la premiÚre ligne est reçue.
  • NĂ©anmoins, mĂȘme avec les limitations dĂ©crites, les agrĂ©gats personnalisĂ©s vous permettent de calculer n'importe quoi.
  • Il est important que les agrĂ©gats puissent ĂȘtre parallĂ©lisĂ©s , au moins PostgreSQL et Oracle (Enterprise Edition) peuvent le faire. Pour cela, la vĂ©ritĂ© devra apprendre Ă  sĂ©rialiser / dĂ©sĂ©rialiser les Ă©tats intermĂ©diaires et Ă©galement les figer reçus de diffĂ©rents flux.

Fonctions de fenĂȘtre


Les fonctions de fenĂȘtre sont apparues dans la norme SQL: 2003 . À l'heure actuelle, ils sont pris en charge par tous les systĂšmes ci-dessus. En substance, les fonctions de fenĂȘtre sont une extension du travail avec les unitĂ©s. Et, bien sĂ»r, les fonctions d'agrĂ©gation personnalisĂ©es fonctionnent Ă©galement dans un contexte fenĂȘtrĂ©.

L'extension est la suivante. Et avant SQL: 2003, les fonctions d'agrĂ©gation fonctionnaient dans une certaine fenĂȘtre, qui Ă©tait soit l'ensemble des rĂ©sultats, soit sa partie, correspondant Ă  la combinaison des valeurs de champ de l'expression GROUP BY. L'utilisateur dispose dĂ©sormais d'une certaine libertĂ© pour manipuler cette fenĂȘtre.

La diffĂ©rence est que les valeurs calculĂ©es Ă  l'aide des fenĂȘtres sont ajoutĂ©es Ă  la sortie dans une colonne distincte et ne nĂ©cessitent pas que le flux entier se rĂ©duise Ă  l'aide des fonctions d'agrĂ©gation. Ainsi, dans une demande, vous pouvez utiliser plusieurs agrĂ©gats de fenĂȘtres chacun dans son propre contexte (fenĂȘtre). Il pouvait y avoir plusieurs fonctions agrĂ©gĂ©es auparavant, mais elles fonctionnaient toutes dans une seule fenĂȘtre.

Grands coups

  • Plus ()
    la fenĂȘtre est l'ensemble des rĂ©sultats. Supposons que la requĂȘte « select count (1) from Samples » renvoie 169. Dans ce cas, en exĂ©cutant « select count (1) over () from Samples », nous obtenons une colonne qui est Ă©crite 169 fois 169 fois.
  • OVER (PARTITION BY)
    il s'agit d'un analogue de GROUP BY, pour chaque combinaison de valeurs, une fenĂȘtre est créée dans laquelle des fonctions d'agrĂ©gation sont exĂ©cutĂ©es. Disons que dans la table Samples, une colonne entiĂšre est val, les donnĂ©es sont des nombres de 1 Ă  169.
    Ensuite, la requĂȘte « sĂ©lectionner le nombre (1) sur (partitionner par (12 + val) / 13) Ă  partir des Ă©chantillons » renverra une colonne dans laquelle la valeur 13 est Ă©crite 169 fois.
  • PLUS (COMMANDER PAR)
    peut ĂȘtre combinĂ© avec PARTITION BY, vous permet de modifier dynamiquement la taille de la fenĂȘtre pendant le curseur, dans ce cas, la fenĂȘtre s'Ă©tend du dĂ©but du groupe Ă  la position actuelle du curseur. Par consĂ©quent, pour le groupe, il s'avĂšre que ce n'est pas la mĂȘme valeur dans la colonne agrĂ©gĂ©e, mais la sienne. Pratique pour calculer les montants cumulĂ©s. RĂ©sultat de la requĂȘte
    'sélectionner la somme (val) sur (ordre par val) des échantillons ' sera une colonne dans laquelle le niÚme élément contiendra la somme des nombres naturels de 1 à n.
  • PLUS (RANGS)
    vous permet de dĂ©finir les cadres de fenĂȘtre, Ă  partir de la position du curseur ou du dĂ©but / fin de la plage ORDER BY.

    Par exemple, ' ... ROWS 1 PRECEDING ... ' signifie que la fenĂȘtre se compose de la ligne actuelle et de 1 avant celle-ci. A ' ... RANGS ENTRE 1 SUIVANT ET 2 SUIVANTS ... ' - la fenĂȘtre se compose de deux lignes immĂ©diatement aprĂšs le curseur.

    LIGNE ACTUELLE dans ce mode indique la position actuelle du curseur. Par exemple, « RANGS ENTRE RANGÉE ACTUELLE ET SANS LIMITE SUIVANTE » signifie de la ligne actuelle jusqu'Ă  la fin de la plage.
  • OVER (RANGE)
    diffĂšre de ROWS dans la mesure oĂč CURRENT ROW signifie ici comme dĂ©but de la fenĂȘtre le dĂ©but de la plage de ORDER BY, et comme fin de la fenĂȘtre - la derniĂšre ligne de la plage ORDER BY.

La syntaxe d'utilisation des fonctions de fenĂȘtre sur diffĂ©rents systĂšmes est lĂ©gĂšrement diffĂ©rente.

Pour résumer ce qui précÚde, il reste un sentiment légÚrement douloureux que les développeurs, aprÚs avoir analysé la construction de divers rapports en SQL, aient mis en évidence les cas les plus courants et les aient concrétisés dans la syntaxe.

Fonctions de retour d'enregistrement


Dans la sortie des fonctions d'agrĂ©gation / fenĂȘtre, chaque ligne rĂ©sultante correspond Ă  une certaine plage de lignes du flux de donnĂ©es entrant. Dans la vie, une telle correspondance n'existe pas toujours.

Par exemple, il est nĂ©cessaire de construire une matrice de covariance 10X10 (pour cela, il faudrait 672X672). Cela peut ĂȘtre fait en un seul passage, pour cela nous exĂ©cutons la fonction d'agrĂ©gation Ă©crite par nous avec 10 paramĂštres numĂ©riques. Le rĂ©sultat de son travail est un jeu d'enregistrements de 10 lignes de 10 valeurs, chaque Ă©lĂ©ment de matrice se rĂ©fĂšre Ă  toutes les lignes du flux d'entrĂ©e (peu importe le nombre).

Nous pouvons dire - alors quoi, dans PostgreSQl, par exemple, vous pouvez retourner un tableau à deux dimensions à partir d'une fonction (Ex: 'ARRAY [[1,2], [3,4]'). Ou sérialisez simplement la matrice en ligne.

C'est bien, mais il n'est pas toujours possible de maintenir la taille du résultat dans le cadre acceptable pour cette approche.

Digression lyrique
Par exemple, notre tùche consiste à généraliser la géométrie.

La taille des géométries nous est inconnue, il peut aussi s'agir du littoral de l'Eurasie à partir de dizaines de millions de points. Ou vice versa, il y a une géométrie trÚs grossiÚre, vous devez la lisser avec des splines. Je voudrais passer les paramÚtres à l'agrégat et obtenir le flux de données au lieu d'un vecteur ou d'une chaßne.

Vous pouvez, bien sûr, dire que le problÚme est tiré par les cheveux, que personne ne le fait, les géométries dans le SGBD sont stockées de maniÚre spéciale, il existe des programmes spéciaux pour le traitement des géométries, ...

En fait, il est assez pratique de stocker des géométries dans des tables réguliÚres point par point, ne serait-ce que parce qu'en déplaçant un point, il n'est pas nécessaire de réécrire l'intégralité du blob. Avant que des données spatiales ne soient divulguées partout dans le SGBD, c'était, par exemple, dans ArcSDE .

DĂšs que la taille moyenne du blob de gĂ©omĂ©trie dĂ©passe la taille de la page, il devient plus rentable de travailler directement avec des points. S'il y avait une opportunitĂ© physique de fonctionner avec un flux de points, peut-ĂȘtre que la roue de l'histoire tournerait Ă  nouveau.

La matrice de covariance n'est pas encore un trĂšs bon exemple de dĂ©synchronisation entre les flux d'entrĂ©e et de sortie, car le rĂ©sultat global est obtenu simultanĂ©ment Ă  la fin. Supposons que vous souhaitiez traiter / compresser un flux de donnĂ©es source. En mĂȘme temps

  • il y a beaucoup de donnĂ©es, elles sont dans le «tas» sans index, en fait elles ont Ă©tĂ© simplement «rapidement» Ă©crites sur le disque
  • vous devez les trier en diffĂ©rentes catĂ©gories, qui sont relativement peu nombreuses
  • dans les catĂ©gories, moyenne sur des intervalles de temps, stocke uniquement la moyenne, le nombre de mesures et la variance
  • tout cela doit ĂȘtre fait rapidement

Quelles sont les options?

  1. Dans SQL, un tri par intervalle de temps / catégorie est requis, ce qui contredit le dernier point.
  2. Si les donnĂ©es sont dĂ©jĂ  triĂ©es par heure (ce qui, en fait, n'est pas garanti), et qu'il sera possible de transmettre ce fait au processeur SQL, vous pouvez le faire avec des fonctions de fenĂȘtre et un passage.
  3. Écrivez une application distincte qui fera tout cela. En PL / SQL ou, plus probablement, Ă©tant donnĂ© qu'il y a beaucoup de donnĂ©es, en C / C ++.
  4. Fonctions qui renvoient des enregistrements. Ils peuvent peut-ĂȘtre nous aider.

Plus de détails sur A.4. Il existe deux mécanismes pour cela: les tables temporaires et les fonctions de pipeline.

  1. Fonctions de convoyeur.
    Ce mécanisme est apparu dans Oracle (à partir du 9i, 2001) et permet à la fonction qui a renvoyé le jeu d'enregistrements de ne pas accumuler de données, mais de les calculer au besoin (par analogie avec la synchronisation de stdout et stdin de deux processus connectés via pipe).
    C'est-Ă -dire Les rĂ©sultats des fonctions en pipeline peuvent commencer Ă  ĂȘtre traitĂ©s avant de quitter cette fonction. Pour cela, il suffit de dire dans la fonction

      FUNCTION f_trans(p refcur_t) RETURN outrecset PIPELINED IS 
 

    et enregistrer les lignes de résultats dans le corps

     LOOP 
 out_rec.var_char1 := in_rec.email; out_rec.var_char2 := in_rec.phone_number; PIPE ROW(out_rec); 
 END LOOP; 

    En conséquence, nous avons

     SELECT * FROM TABLE( refcur_pkg.f_trans( CURSOR(SELECT * FROM employees WHERE department_id = 60))); 

    Les agrégats personnalisés ne sont tout simplement pas nécessaires lorsqu'il existe des fonctions de pipeline.

    Bravo, Oracle!

    Il n'y a pas si longtemps (2014), les fonctions de pipeline sont également apparues dans DB2 (IBM i 7.1 TR9, i 7.2 TR1).
  2. Tables temporaires.
    Pour commencer, il semble que ni MS SQL ni PostgreSQL ne peuvent retourner un curseur à partir d'une fonction d'agrégation.

    Eh bien, par analogie avec les fonctions du pipeline, obtenons le curseur en tant que paramĂštre, le traitons, l'ajoutons Ă  une table temporaire et y retournons le curseur.

    Cependant, dans MS SQL, il n'est pas possible de passer le curseur Ă  une procĂ©dure stockĂ©e par un paramĂštre, il est uniquement possible de crĂ©er un curseur dans la procĂ©dure et de renvoyer le paramĂštre via la sortie. La mĂȘme chose peut ĂȘtre dite pour PostgreSQL.

    Eh bien, il suffit d'ouvrir le curseur, de le soustraire, de traiter les valeurs, de calculer le résultat, de l'ajouter à la table temporaire et de rendre le curseur.

    Ou encore plus simple, nous ajoutons les rĂ©sultats de la requĂȘte Ă  une table temporaire, les traitons et renvoyons les rĂ©sultats via le curseur Ă  une autre table temporaire.

    Que puis-je dire. Tout d'abord et surtout, la lecture des données via le curseur est plus lente que le traitement dans le flux. DeuxiÚmement, pourquoi avez-vous besoin d'un processeur SQL, lisons des tables avec des curseurs, créons des tables temporaires avec nos mains, écrivons la logique de jointure en boucles ... C'est comme des insertions d'assembleur en C / C ++, parfois vous pouvez vous faire plaisir, mais il vaut mieux ne pas en abuser.

Ainsi, aprÚs avoir examiné une question avec des fonctions renvoyant un jeu d'enregistrements, nous arrivons à des conclusions:

  • Les agrĂ©gats personnalisĂ©s ne nous seront pas vraiment utiles ici.
  • Dans tous les cas, vous devrez crĂ©er des objets dans la base de donnĂ©es. Que ce soit des fonctions ou des tables temporaires. Au minimum, des droits appropriĂ©s sont requis. Et je veux juste traiter quelques chiffres.
  • NĂ©anmoins, mĂȘme avec les limitations dĂ©crites, il n'est parfois pas trĂšs Ă©lĂ©gant, mais avec cette mĂ©thode, vous pouvez rĂ©soudre le problĂšme.

Quoi d'autre


En fait, si nous avons déjà la possibilité de résoudre des problÚmes, de quoi d'autre l'auteur a-t-il besoin?
En fait, la machine de Turing peut également calculer n'importe quoi, ce n'est pas trÚs rapide et pas trop pratique.

Nous formulons les exigences comme suit:

  1. ce doit ĂȘtre un opĂ©rateur relationnel utilisable au mĂȘme titre que le reste (sĂ©lection, projection, ...)
  2. ce doit ĂȘtre un opĂ©rateur qui transforme un flux de donnĂ©es en un autre
  3. il n'y a pas de synchronisation entre les flux d'entrée et de sortie
  4. La déclaration de l'opérateur définit la structure du flux de sortie
  5. l'opérateur a la capacité d'initialiser dynamiquement (sous la forme d'une fonction, plus précisément son corps, spécifié directement dans la définition de l'opérateur)
  6. ainsi qu'un destructeur sous forme de fonction (...)
  7. ainsi qu'une fonction (...) qui est appelée à chaque fois qu'une nouvelle ligne est reçue du flux d'entrée
  8. l'opérateur a un contexte d'exécution - un ensemble de variables et / ou de collections définies par l'utilisateur qui sont nécessaires pour le travail
  9. pour exécuter cette instruction, vous n'avez pas besoin de créer d'objets de base de données, vous n'avez pas besoin de droits supplémentaires
  10. tout ce qui est nécessaire au travail est défini en un seul endroit, dans une seule langue

Il Ă©tait une fois, l' auteur a fait un tel opĂ©rateur qui Ă©tend le processeur self-made du sous-ensemble implĂ©mentĂ© de TTM / Tutorial D. Maintenant, la mĂȘme idĂ©e est proposĂ©e pour SQL.

Cela vaut la peine d'ĂȘtre averti, ici SQL se termine et l'improvisation commence. La syntaxe est laissĂ©e telle qu'elle Ă©tait dans l'original, au final, le sucre syntaxique peut ĂȘtre n'importe quoi, il ne change pas l'essence.

Ainsi, l'opérateur de mastication se compose de

  1. Un en-tĂȘte qui contient une liste des champs de sortie et leurs types.
    Chaque champ de sortie (et d'entrée) est une variable locale.
    Ex: "chew {" var1 "float," var2 "integer}" signifie qu'il y aura deux colonnes dans le flux de sortie - un point flottant et un entier
  2. Corps - une liste de rappels pour les Ă©vĂ©nements, pour le moment - le dĂ©but du flux, la fin du flux, la ligne. Par syntaxe, les fonctions sont proches de PL / SQL. La fonction prĂ©dĂ©finie __interrupt () est un analogue de PIPE, elle prend les valeurs des variables correspondant aux colonnes de sortie et les place dans le flux de sortie. Si la mĂ©moire tampon du flux de sortie dĂ©borde, le travail du gestionnaire s'arrĂȘte et le travail du cĂŽtĂ© rĂ©cepteur du flux commence.
    Ex: "hook" init "{var1: = 0; var2: = -1; } "

La façon la plus simple de montrer des exemples.

  • Un analogue de la fonction d'agrĂ©gation SUM.

     --  'select sum(val) from samples' -- select * from samples chew {“sum(val)” float} --    hook “init” { “sum(val)” := 0; --      } hook “row” { if (not isnull("val")) then "sum(val)" := "sum(val)" + "val"; end if; } hook “finit” { call __interrupt(); --  PIPE } 

    Il semble volumineux, mais ce n'est qu'un exemple,
    il n'est pas nécessaire d'écrire un programme C pour ajouter quelques chiffres.
  • SUM + AVG

     --  'select sum(val), avg(val) from samples' -- select * from samples chew { “sum(val)” float, “avg(val)” float --       } hook “init” { “sum(val)” := 0; “avg(val)” := 0; var num integer; num := 0; --    ,       } hook “row” { if (not isnull("val")) then "sum(val)" := "sum(val)" + "val"; num := num + 1; end if; } hook “finit” { if (num > 0) then “avg(val)” := “sum(val)” / num; end if; call __interrupt(); } 

    Ici, nous attirons l'attention sur le fait que la sommation ne se produit qu'une seule fois.
  • SUM + GROUP BY

     --  'select sum(val) from samples group by type' -- select * from --     ( samples val, type from samples order by type ) chew { “sum(val)” float } hook “init” { “sum(val)” := 0; var gtype integer; gtype := NULL; var num integer; --   num := 0; } hook “row” { if (gtype <> “type”) then __interrupt(); “gtype” := type; "sum(val)" := 0; num := 0; end if; if (not isnull("val")) then "sum(val)" := "sum(val)" + "val"; num := num + 1; end if; } hook “finit” { if (num > 0) then call __interrupt(); end if; } 
  • ROW_NUMBER () OVER ()

     -- select row_number() over() as num, * from samples -- select * from samples chew { “num” integer, * --        --   '* except val1, ...valX',   TTM } hook “init” { num := 0; } hook “row” { num := num + 1; call __interrupt(); } 

Est-il possible de proposer un exemple sur lequel cette approche donne des résultats fondamentalement inaccessibles de la maniÚre habituelle? Nous les avons.

Parfois, il arrive que les donnĂ©es soient presque triĂ©es. Ils peuvent mĂȘme ĂȘtre complĂštement triĂ©s, mais ce n'est pas sĂ»r.

( ) . C'est-Ă -dire T1 T2 T1 < T2.

, T1 T2 () , ( ) .

, , , , .

.

, .

.

, .
, .

SQL- .

lambda- SQL- , , .

Conclusion


.

PL/SQL.

.

, , GROUP BY.

, , SQL- .

, , .

PS: .

Source: https://habr.com/ru/post/fr435120/


All Articles