Worum es in dem Artikel geht, und so impliziert der Name.
Darüber hinaus wird der Autor erklären, warum dies aus seiner Sicht notwendig ist, und erklären, dass SUBJ nicht nur eine modische Technologie ist, sondern auch „ein doppelt notwendiges Geschäft - sowohl angenehm als auch nützlich“.
Es ist immer wieder interessant zu sehen, wie mehrere talentierte Leute etwas tun (eine Programmiersprache, warum nicht) und genau wissen, welches Problem sie lösen und welche Aufgaben sie sich stellen. Und testen Sie auch ihre Kreation an sich selbst. Es kann nicht mit den monumentalen Kreationen riesiger Komitees verglichen werden, die die Aufrechterhaltung der Harmonie des Universums in den Vordergrund stellen und wer sie versteht.
Vergleichen Sie zum Beispiel das Schicksal von
FORTRAN und
PL / 1 . Wer wird sich jetzt an diesen PL / 1 erinnern?
Aus dieser Sicht ist
AWK beispielsweise sehr erfolgreich. Es ist erwähnenswert, dass in seinem Namen A
Alfred Aho , einer der Autoren des
Drachenbuchs , W
Peter Weinberger ist , der an Fortran-77 beteiligt war, K
Brian Kernigan ist , wo er ohne ihn wäre. Die Sprache ist für die Verarbeitung von Textströmen im laufenden Betrieb in Pipes zwischen Prozessen vorgesehen.
Die Sprache ist typenlos (
dies ist nicht ganz richtig ), ihre Syntax ist C sehr ähnlich, sie verfügt über Filterfunktionen, assoziative Arrays, Stream-Start / Ende-Ereignisse, Newline-Ereignisse ...
Der Autor war von dieser Sprache immer beeindruckt, auch von der Tatsache, dass sein Interpreter nicht installiert werden muss, unter UNIX-ähnlichen Systemen immer vorhanden ist und unter Windows ausreicht, nur die ausführbare Datei zu kopieren und alles funktioniert. Dies ist jedoch nicht der Fall.
Dabei muss der Autor das SQL + AWK-Bundle häufig verwenden, und deshalb. SQL ist nach wie vor eine deklarative Sprache zur Steuerung des Datenflusses. Es bietet nur sehr begrenzte Möglichkeiten, mit dem Kontext der Abfrageausführung in Form von Aggregatfunktionen zu arbeiten.
Wie kann man beispielsweise ein zweidimensionales Histogramm mit SQL erstellen?
Angenommen, die Verwendung von GROUP BY impliziert das Sortieren. Wenn Sie Hunderte von Millionen (oder sogar mehr) Zeilen haben, ist dies kein billiges Vergnügen.
UPD: In den Kommentaren haben sie mich korrigiert, dass dies nicht ganz richtig ist (oder überhaupt nicht)Der SQL-Prozessor kann beim Erstellen eines Hashs gemäß dem Gruppierungskriterium Aggregatfunktionen ausführen. Dazu ist es notwendig, dass es über genügend freien Speicher verfügt, um die Hash-Map im Speicher abzulegen.
Dann werden die Kontexte der Gruppen aktualisiert, wenn die Tabelle gelesen wird, und am Ende dieses Lesens haben wir bereits das berechnete Ergebnis.
Die gleiche Technik kann auf Fensterfunktionen (unten) erweitert werden, nur der Kontext wird "dicker".
Wenn die Anzahl der Gruppen im Voraus unbekannt oder sehr groß ist, muss der SQL-Prozessor einen temporären Index erstellen und diesen in einem zweiten Durchgang ausführen.
In einfachen Fällen, wie hier, wie hier - ein einfacher COUNT, eine universelle Option ist möglich - ein temporärer Index (cx, cy, count), dann wird bei einer kleinen Anzahl von Gruppen alles auf zwischengespeicherten Seiten gespeichert. In komplexen Fällen, Fensterfunktionen, wird der Zustand der Gruppe nicht trivial und die ständige (De-) Serialisierung entspricht überhaupt nicht den Anweisungen des Arztes.
Zusammenfassung: Der SQL-Prozessor greift zum Sortieren zurück, wenn er die Anzahl der Gruppen nach GROUP BY nicht schätzen kann. Die Gruppierung nach berechneten Werten ist jedoch (oft) genau der Fall.
Deshalb müssen Sie etwas tun wie:
psql -t -q -c 'select x, y from samples' | gawk -f mk_hist2d.awk
Dabei sammelt mk_hist2d.awk Statistiken im assoziativen Array und zeigt sie nach Abschluss der Arbeit an
# 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"; } }
Es gibt einen ABER - der gesamte Datenstrom muss vom Server an die Arbeitsmaschine gesendet werden, und das ist nicht so billig.
Ist es möglich, das Angenehme mit dem Nützlichen zu kombinieren - um während der Ausführung der SQL-Abfrage Statistiken zu sammeln, ohne jedoch zu sortieren? Ja, zum Beispiel mit benutzerdefinierten Aggregatfunktionen.
Benutzerdefinierte Aggregatfunktionen
Subj ist in verschiedenen Systemen vorhanden, überall wird es ein wenig auf seine eigene Weise gemacht.
- PostgreSQL Die Dokumentation finden Sie hier . Weitere Details hier .
Hier wird der maximale Kontostand berechnet.
Und dies ist ein Beispiel , das berechnet, was mehr in der booleschen Spalte steht - wahr oder falsch.
Es sieht so aus -
CREATE AGGREGATE mode(boolean) ( SFUNC = mode_bool_state, STYPE = INT[], FINALFUNC = mode_bool_final, INITCOND = '{0,0}' );
Hier ist SFUNC eine Funktion, die für jede Zeile im Stream aufgerufen wird.
Das erste Argument ist vom Typ STYPE .
FINALFUNC wird verwendet, um die Berechnungen abzuschließen und den Wert des Aggregats zurückzugeben.
INITCOND - Initialisierung des Anfangswertes des internen Zustands ( STYPE ), der als erstes Argument übergeben wurde.
Da Funktionen in C geschrieben werden können (was bedeutet, dass Sie für den internen Status den Speicher verwenden können, der beim Schließen der Anforderung automatisch freigegeben wird), ist dies ein sehr leistungsfähiges Tool. Außerhalb des Anwendungsbereichs muss man noch gehen können. - MS SQL
Zuvor (2000) war es vor der Anforderung erforderlich, ein ActiveX-Objekt zu erstellen, um die Aggregation mit diesem Objekt durchzuführen.
Jetzt (2016+) erfolgt dies in der CLR-Umgebung. Sie müssen eine benutzerdefinierte Funktion erstellen, eine Baugruppe erstellen und registrieren. Dann können Sie ein Aggregat erstellen.
Ein Beispiel für die Berechnung des geometrischen Mittelwerts sowie für das Zusammenführen von Zeichenfolgen: mit zusätzlichen Parametern und einem benutzerdefinierten Typ zum Speichern eines Zwischenzustands. - Oracle
In Oracle erfolgt dies mithilfe der ODCIAggregate-Datenkassette (Schnittstelle).
Um ein eigenes Aggregat zu erstellen, müssen Sie einen benutzerdefinierten Typ schreiben, der 4 Methoden implementiert
- Die statische Initialisierung (ODCIAggregateInitialize) sollte eine Instanz des gewünschten Typs erstellen und über den Parameter zurückkehren
- Iterationen (ODCIAggregateIterate), die für jede Datenzeile aufgerufen werden
- Merge (ODCIAggregateMerge), mit dem parallel ausgeführte Aggregate zusammengeführt werden
- finish (ODCIAggregateTerminate) - Ergebnisausgabe
Beispiele: 1 , 2 , 3 , 4 . - DB2
Es gibt keine explizite Möglichkeit, benutzerdefinierte Aggregate in DB2 zu verwenden.
Sie können jedoch eine Standardfunktion (wenn auch MAX) in einen benutzerdefinierten Typ (in Java) einfügen und das System dazu bringen, Abfragen des Formulars auszuführen
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)
Was ist in all diesen Systemen bemerkenswert?
- Auf die eine oder andere Weise müssen Sie einige Objekte in der Datenbank erstellen. Sei es AGGREGATE oder TYPE. Zumindest sind entsprechende Rechte erforderlich. Und möchte nur ein paar Zahlen auf sein Knie setzen.
- Möglicherweise müssen Sie etwas in einer anderen Sprache schreiben, sei es C, C # oder Java.
Um das, was geschrieben ist, in das System zu integrieren, sind wiederum Rechte erforderlich. Aber alles was ich will ...
- Schwierigkeiten beim Initialisieren. Angenommen, Sie möchten Histogramme mit unterschiedlichen Korbgrößen lesen. Es scheint einfacher zu sein - wir geben die gewünschte INITCOND an, wenn wir das Aggregat (PostgreSQL) und das gesamte Unternehmen deklarieren. Aber dann benötigen Sie für jede Größe des Warenkorbs Ihr eigenes Aggregat, und auch hierfür sind die Rechte erforderlich.
Hier können Sie auf einen schmutzigen Trick zurückgreifen und den Union-Prozessor aus der Initialisierungszeile (vorwärts) und den Daten entfernen. Erstellen Sie den Kontext nicht im Konstruktor, sondern wenn die erste Zeile empfangen wird.
- Trotz der beschriebenen Einschränkungen können Sie mit benutzerdefinierten Aggregaten alles berechnen.
- Es ist wichtig, dass Aggregate parallelisiert werden können , zumindest PostgreSQL, und Oracle (Enterprise Edition) kann dies tun. Dazu muss die Wahrheit lernen, wie man Zwischenzustände serialisiert / deserialisiert und sie aus verschiedenen Streams einfriert.
Fensterfunktionen
Fensterfunktionen wurden im
SQL: 2003- Standard angezeigt. Im Moment werden sie von allen oben genannten Systemen unterstützt. Fensterfunktionen sind im Wesentlichen eine Erweiterung der Arbeit mit Einheiten. Benutzerdefinierte Aggregatfunktionen funktionieren natürlich auch in einem Fensterkontext.
Die Erweiterung ist dies. Vor SQL: 2003 arbeiteten Aggregatfunktionen in einem bestimmten Fenster, das entweder die gesamte Ergebnismenge oder deren Teil war und der Kombination von Feldwerten aus dem GROUP BY-Ausdruck entsprach. Der Benutzer hat jetzt einige Freiheiten bei der Bearbeitung dieses Fensters.
Der Unterschied besteht darin, dass die mithilfe der Fenster berechneten Werte in einer separaten Spalte zur Ausgabe hinzugefügt werden und nicht erforderlich ist, dass der gesamte Stream mithilfe von Aggregatfunktionen zusammenfällt. In einer Anfrage können Sie also mehrere Fensteraggregate jeweils in einem eigenen Kontext (Fenster) verwenden. Es konnte vorher mehrere Aggregatfunktionen geben, aber alle funktionierten in einem Fenster.
Große Striche
- Over ()
Das Fenster ist die gesamte Ergebnismenge. Angenommen, die Abfrage ' select count (1) from Samples ' gibt 169 zurück. In diesem Fall erhalten wir beim Ausführen von ' select count (1) over () from Samples ' eine Spalte, die 169-mal 169-mal geschrieben wird. - ÜBER (TEILUNG DURCH)
Es ist ein Analogon zu GROUP BY. Für jede Wertekombination wird ein Fenster erstellt, in dem Aggregatfunktionen ausgeführt werden. Angenommen, in der Samples-Tabelle ist eine Ganzzahlspalte val, die Daten sind Zahlen von 1 bis 169.
Dann gibt die Abfrage ' select count (1) over (Partition by (12 + val) / 13) from Samples ' eine Spalte zurück, in die der Wert 13 169 Mal geschrieben wird.
- ÜBER (BESTELLEN NACH)
kann mit PARTITION BY kombiniert werden, ermöglicht es Ihnen, die Größe des Fensters während des Cursors dynamisch zu ändern. In diesem Fall erstreckt sich das Fenster vom Anfang der Gruppe bis zur aktuellen Cursorposition. Infolgedessen ergibt sich für die Gruppe nicht derselbe Wert in der Aggregatspalte, sondern der eigene. Praktisch für die Berechnung der kumulierten Beträge. Abfrageergebnis
'Summe (Wert) über (Reihenfolge nach Wert) aus Stichproben auswählen ' ist eine Spalte, in der das n-te Element die Summe der natürlichen Zahlen von 1 bis n enthält. - ÜBER (REIHEN)
Mit dieser Option können Sie die Fensterrahmen ausgehend von der Cursorposition oder dem Anfang / Ende des ORDER BY-Bereichs definieren.
Beispiel: " ... REIHEN 1 VORHER ... " bedeutet, dass das Fenster aus der aktuellen Zeile und 1 davor besteht. A ' ... REIHEN ZWISCHEN 1 FOLGENDEN UND 2 FOLGENDEN ... ' - Das Fenster besteht aus zwei Zeilen unmittelbar nach dem Cursor.
AKTUELLE REIHE in diesem Modus zeigt die aktuelle Cursorposition an. Zum Beispiel bedeutet " REIHEN ZWISCHEN AKTUELLER REIHE UND UNBEGRENZTEM FOLGENDEM " von der aktuellen Zeile bis zum Ende des Bereichs. - ÜBER (BEREICH)
unterscheidet sich von ROWS darin, dass CURRENT ROW hier als Anfang des Fensters den Beginn des Bereichs von ORDER BY und als Ende des Fensters - die letzte Zeile des Bereichs ORDER BY - bedeutet.
Die Syntax für die Verwendung von Fensterfunktionen auf verschiedenen Systemen unterscheidet sich geringfügig.
Zusammenfassend lässt sich sagen, dass die Entwickler nach der Analyse der Erstellung verschiedener Berichte in SQL die häufigsten Fälle hervorgehoben und in der Syntax genau konkretisiert haben.
Funktionen zum Zurückgeben von Datensätzen
Bei der Ausgabe von Aggregat- / Fensterfunktionen entspricht jede resultierende Zeile einem bestimmten Zeilenbereich aus dem eingehenden Datenstrom. Im Leben existiert eine solche Entsprechung nicht immer.
Zum Beispiel ist es erforderlich, eine Kovarianzmatrix 10X10 zu konstruieren (
dafür würde es 672X672 dauern). Dies
kann in einem Durchgang erfolgen, dazu führen wir die von uns geschriebene Aggregatfunktion mit 10 numerischen Parametern aus. Das Ergebnis ihrer Arbeit ist ein Recordset von 10 Zeilen mit 10 Werten. Jedes Matrixelement bezieht sich auf alle Zeilen des Eingabestreams (egal wie viele es gibt).
Wir können sagen - also, was, zum Beispiel, in PostgreSQl
können Sie ein zweidimensionales Array von einer Funktion zurückgeben (Beispiel: 'ARRAY [[1,2], [3,4]'). Oder serialisieren Sie die Matrix einfach in eine Zeile.
Es ist gut, aber es ist nicht immer möglich, die Größe des Ergebnisses innerhalb des für einen solchen Ansatz akzeptablen Rahmens zu halten.
Lyrischer ExkursUnsere Aufgabe ist es beispielsweise, die Geometrie zu verallgemeinern.
Die Größe der Geometrien ist uns unbekannt, es kann auch die Küste Eurasiens von zig Millionen Punkten sein. Oder umgekehrt, es gibt eine sehr grobe Geometrie, die Sie mit Splines glätten müssen. Ich möchte die Parameter an das Aggregat übergeben und den Datenstrom anstelle eines Vektors oder einer Zeichenfolge abrufen.
Man kann natürlich sagen, dass das Problem weit hergeholt ist, dass es niemand tut, die Geometrien im DBMS auf besondere Weise gespeichert werden, es gibt spezielle Programme zur Verarbeitung von Geometrien, ...
Tatsächlich ist es sehr praktisch, Geometrien in regulären Tabellen punktweise zu speichern, schon allein deshalb, weil durch Verschieben eines Punkts nicht der gesamte Blob neu geschrieben werden muss. Bevor räumliche Daten überall im DBMS durchgesickert sind, war dies beispielsweise in
ArcSDE der Fall.
Sobald die durchschnittliche Größe des Geometrie-Blobs die Seitengröße überschreitet, wird es rentabler, direkt mit Punkten zu arbeiten. Wenn es eine physische Möglichkeit gäbe, mit einem Fluss von Punkten zu arbeiten, würde sich das Rad der Geschichte vielleicht wieder drehen.
Die Kovarianzmatrix ist immer noch kein sehr gutes Beispiel für die Desynchronisation zwischen den Eingangs- und Ausgangsströmen, da das gesamte Ergebnis am Ende gleichzeitig erhalten wird. Angenommen, Sie möchten einen Quelldatenstrom verarbeiten / komprimieren. Dabei
- Es gibt viele Daten, sie befinden sich auf dem "Haufen" ohne Indizes, tatsächlich wurden sie einfach "schnell" auf die Festplatte geschrieben
- Sie müssen sie in verschiedene Kategorien sortieren, die relativ wenige sind
- Innerhalb von Kategorien Durchschnitt über Zeitintervalle, nur Durchschnitt, Anzahl der Messungen und Varianz speichern
- All dies muss schnell erledigt werden
Welche Möglichkeiten gibt es?
- In SQL ist eine Sortierung nach Zeitintervall / Kategorie erforderlich, was dem letzten Punkt widerspricht.
- Wenn die Daten bereits nach Zeit sortiert sind (was tatsächlich nicht garantiert ist) und diese Tatsache an den SQL-Prozessor übermittelt werden kann, können Sie Fensterfunktionen und einen Durchgang verwenden.
- Schreiben Sie eine separate Anwendung, die all dies erledigt. In PL / SQL oder wahrscheinlicher in C / C ++, da viele Daten vorhanden sind.
- Funktionen, die Datensätze zurückgeben. Vielleicht können sie uns helfen.
Weitere Details zu A.4. Hierfür gibt es zwei Mechanismen: temporäre Tabellen und Pipeline-Funktionen.
- Fördererfunktionen.
Dieser Mechanismus wurde in Oracle (ab 9i, 2001) eingeführt und ermöglicht es der Funktion, die das Recordset zurückgegeben hat, Daten nicht zu akkumulieren, sondern nach Bedarf zu berechnen (analog zur Synchronisation von stdout und stdin zweier über Pipe verbundener Prozesse).
Das heißt, Die Ergebnisse von Pipeline-Funktionen werden möglicherweise verarbeitet, bevor diese Funktion beendet wird. Dazu reicht es aus, in der Definition der Funktion zu sagen
FUNCTION f_trans(p refcur_t) RETURN outrecset PIPELINED IS …
und Ergebniszeilen im Körper registrieren
LOOP … out_rec.var_char1 := in_rec.email; out_rec.var_char2 := in_rec.phone_number; PIPE ROW(out_rec); … END LOOP;
Als Ergebnis haben wir
SELECT * FROM TABLE( refcur_pkg.f_trans( CURSOR(SELECT * FROM employees WHERE department_id = 60)));
Benutzerdefinierte Aggregate werden bei Pipelinefunktionen einfach nicht benötigt.
Bravo, Orakel!
Vor nicht allzu langer Zeit (2014) wurden Pipeline-Funktionen auch in DB2 (IBM i 7.1 TR9, i 7.2 TR1) angezeigt. - Temporäre Tische.
Zunächst scheint es, dass weder MS SQL noch PostgreSQL einen Cursor von einer Aggregatfunktion zurückgeben können.
Lassen Sie uns in Analogie zu den Pipeline-Funktionen den Cursor als Parameter abrufen, verarbeiten, einer temporären Tabelle hinzufügen und den Cursor darauf zurücksetzen.
In MS SQL ist es jedoch nicht möglich , den Cursor durch einen Parameter an eine gespeicherte Prozedur zu übergeben. Es ist nur möglich, einen Cursor in der Prozedur zu erstellen und den Parameter über die Ausgabe zurückzugeben. Gleiches gilt für PostgreSQL.
Nun gut, öffnen Sie einfach den Cursor, subtrahieren Sie ihn, verarbeiten Sie die Werte, berechnen Sie das Ergebnis, fügen Sie es der temporären Tabelle hinzu und rendern Sie den Cursor.
Oder noch einfacher: Wir fügen die Abfrageergebnisse einer temporären Tabelle hinzu, verarbeiten sie und geben die Ergebnisse über den Cursor an eine andere temporäre Tabelle zurück.
Was soll ich sagen Erstens und vor allem ist das Lesen von Daten über den Cursor langsamer als das Verarbeiten im Stream. Zweitens, warum brauchen Sie dann überhaupt einen SQL-Prozessor? Lassen Sie uns Tabellen mit Cursorn lesen, temporäre Tabellen mit unseren Händen erstellen, Verknüpfungslogik in Schleifen schreiben ... Es ist wie Assembler-Einfügungen in C / C ++, manchmal können Sie sich selbst behandeln, aber es ist besser, sie nicht zu missbrauchen.
Nachdem wir eine Frage mit Funktionen betrachtet haben, die Recordset zurückgeben, kommen wir zu folgenden Schlussfolgerungen:
- Benutzerdefinierte Aggregate helfen uns hier nicht wirklich.
- In jedem Fall müssen Sie einige Objekte in der Datenbank erstellen. Sei es Funktionen oder temporäre Tabellen. Zumindest sind entsprechende Rechte erforderlich. Und möchte nur ein paar Zahlen verarbeiten.
- Trotz der beschriebenen Einschränkungen ist es manchmal nicht sehr elegant, aber mit dieser Methode können Sie das Problem lösen.
Was noch
Was braucht der Autor noch, wenn wir bereits die Möglichkeit haben, Probleme zu lösen?
Eigentlich kann die Turingmaschine auch alles berechnen, macht es einfach nicht sehr schnell und nicht zu bequem.
Wir formulieren die Anforderungen wie folgt:
- Es muss sich um einen Vergleichsoperator handeln, der mit den anderen vergleichbar ist (Auswahl, Projektion, ...).
- Es muss ein Operator sein, der einen Datenstrom in einen anderen umwandelt
- Es gibt keine Synchronisation zwischen Eingangs- und Ausgangsströmen
- Die Operatordeklaration definiert die Struktur des Ausgabestreams
- Der Operator hat die Möglichkeit, dynamisch zu initialisieren (in Form einer Funktion, genauer gesagt ihres Körpers, der direkt in der Definition des Operators angegeben ist).
- sowie ein Destruktor in Form einer Funktion (...)
- sowie eine Funktion (...), die jedes Mal aufgerufen wird, wenn eine neue Zeile vom Eingabestream empfangen wird
- Der Operator verfügt über einen Ausführungskontext - einen benutzerdefinierten Satz von Variablen und / oder Sammlungen, die für die Arbeit benötigt werden
- Um diesen Operator auszuführen, müssen Sie keine Datenbankobjekte erstellen und benötigen keine zusätzlichen Rechte
- Alles, was für die Arbeit benötigt wird, wird an einem Ort in einer Sprache definiert
Es war einmal
, dass der Autor einen solchen Operator erstellt hat, der den selbst erstellten Prozessor der implementierten Teilmenge von
TTM / Tutorial D erweitert. Jetzt wird die gleiche Idee für SQL vorgeschlagen.
Es ist eine Warnung wert, hier endet SQL und die Improvisation beginnt. Die Syntax bleibt wie im Original, am Ende kann syntaktischer Zucker alles sein, es ändert nichts an der Essenz.
Der
Kauoperator besteht also aus
- Ein Header, der eine Liste der Ausgabefelder und ihrer Typen enthält.
Jedes Ausgabe- (und Eingabe-) Feld ist eine lokale Variable.
Beispiel: "Kauen {" var1 "float," var2 "Ganzzahl}" bedeutet, dass der Ausgabestream zwei Spalten enthält - einen Gleitkomma- und eine Ganzzahl - Körper - eine Liste von Rückrufen für Ereignisse im Moment - der Beginn des Streams, das Ende des Streams, die Zeile. Nach der Syntax liegen die Funktionen in der Nähe von PL / SQL. Die vordefinierte Funktion __interrupt () ist ein Analogon zu PIPE. Sie nimmt Werte aus den Variablen, die den Ausgabespalten entsprechen, und platziert sie im Ausgabestream. Wenn der Puffer des Ausgabestreams voll ist, wird der Handler angehalten und die empfangende Seite des Streams beginnt zu arbeiten.
Beispiel: "hook" init "{var1: = 0; var2: = -1; } "
Der einfachste Weg, Beispiele zu zeigen.
- Ein Analogon der Aggregatfunktion SUM.
Es sieht sperrig aus, ist aber nur ein Beispiel.
Es ist nicht erforderlich, ein C-Programm zu schreiben, um einige Zahlen hinzuzufügen. - SUM + AVG
Hier machen wir darauf aufmerksam, dass die Summierung nur einmal erfolgt. - SUM + GROUP BY
- ROW_NUMBER () OVER ()
Ist es möglich, ein Beispiel anzubieten, anhand dessen dieser Ansatz Ergebnisse liefert, die auf übliche Weise grundsätzlich unerreichbar sind? Wir haben sie.
Manchmal kommt es vor, dass die Daten fast sortiert sind. Sie können sogar vollständig sortiert sein, aber es ist nicht sicher bekannt.
Angenommen, im obigen Beispiel (Datenstromkomprimierung) stammen die Daten aus verschiedenen Quellen und können aus verschiedenen Gründen leicht gemischt werden. Das heißt,
Eine Zeile von einer Quelle mit dem Zeitstempel T1 kann sich in der Datenbank nach einer Zeile von einer anderen Quelle mit der Zeit T2 befinden, während T1 <T2 ist.Selbst wenn wir garantieren, dass der Unterschied zwischen T1 und T2 niemals eine bestimmte (spärliche) Konstante überschreitet, können wir hier nicht (auf herkömmliche Weise) auf Sortierung verzichten.Unter Verwendung des vorgeschlagenen Ansatzes ist es jedoch möglich, den Eingabestrom zu puffern und die Daten des aktuellen Zeitintervalls erst zu verarbeiten, nachdem die Eingabe Zeilen mit einem Zeitstempel empfangen hat, der mindestens eine gegebene Konstante um die rechte Grenze des Intervalls überschreitet.Hier gibt es einen sehr wichtigen Punkt.Nur wir wissen, dass die Daten fast sortiert sind.Nur wir kennen den Wert dieser Konstante.Diese Konstante ist nur für dieses Problem und möglicherweise nur für dieses Experiment charakteristisch.Und wir verwenden diesen Hack in eigener Verantwortung, um ein Sortieren zu vermeiden.Unser Standardwissen über die Aufgabe ist nicht in der Regel vorhanden, um es dem SQL-Prozessor mitzuteilen, und es ist schwer vorstellbar.Die Verwendung von Lambda-Funktionen bietet eine universelle Möglichkeit, den SQL-Prozessor zu zwingen, genau das zu tun, was wir brauchen, genau dort, wo wir es brauchen.Fazit
Das vorgeschlagene Design scheint nicht sehr schwierig zu implementieren zu sein.In jedem Fall mit gültigem PL / SQL.Die Idee selbst ist einfach und intuitiv und fügt der Sprache keine neuen Entitäten hinzu.Dies ist eine einzelne Einheit, die bei Bedarf die Aggregat- und Fensterfunktionen GROUP BY ersetzt.Ein Mechanismus, mit dem Sie auf Sortierungen verzichten können, bei denen ein herkömmlicher SQL-Prozessor keine Möglichkeit bietet.Vor allem aber ist es ein Mechanismus, der Ihnen die Freiheit gibt, mit den Daten alles zu tun, was Sie wollen.PS: Vielen Dank an Dorofei Proleskovsky für die Teilnahme an der Vorbereitung des Artikels.