Ausgewogenheit von Lese- und Schreibzugriff in der Datenbank

Ausgewogenheit von Lese- und Schreibzugriff in der Datenbank
Im vorherigen Artikel Ich habe das Konzept und die Implementierung einer Datenbank beschrieben, die auf Funktionen und nicht auf Tabellen und Feldern wie bei relationalen Datenbanken basiert. Dabei wurden zahlreiche Beispiele gegeben, die die Vorteile dieses Ansatzes gegenüber dem klassischen aufzeigen. Viele empfanden sie als nicht überzeugend genug.

In diesem Artikel zeige ich, wie ein solches Konzept schnell und bequem das Gleichgewicht zwischen Schreiben und Lesen in der Datenbank ermöglicht, ohne die Logik der Funktionsweise zu ändern. Eine ähnliche Funktionalität wurde in modernen kommerziellen DBMS (insbesondere Oracle und Microsoft SQL Server) versucht umzusetzen. Am Ende des Artikels werde ich zeigen, dass das Ergebnis, um es milde auszudrücken, nicht besonders gut gelungen ist.

Beschreibung

Wie zuvor beginne ich zur besseren Verständlichkeit mit Beispielen. Angenommen, wir müssen eine Logik implementieren, die eine Liste von Abteilungen mit der Anzahl der Mitarbeiter in diesen Abteilungen und deren Gesamtsalary zurückgibt.

In einer funktionalen Datenbank würde das folgendermaßen aussehen:

CLASS Abteilung 'Abteilung';
name 'Bezeichnung' = DATENSTRING[100] (Abteilung);

CLASS Employee 'Mitarbeiter';
department 'Abteilung' = DATA Department (Employee);
salary 'Gehalt' = DATA NUMERIC[10,2] (Employee);

countEmployees 'Anzahl Mitarbeiter' (Department d) = 
    GRUPPE SUMME 1 WENN abteilung(Mitarbeiter e) = d;
salarySum 'Gesamtes Gehalt' (Department d) = 
    GRUPPE SUMME Gehalt (Mitarbeiter e), WENN abteilung(e) = d;

SELECT name(Department d), countEmployees(d), salarySum(d);

Die Komplexität der Ausführung dieser Abfrage in jedem DBMS wird äquivalent sein zu O(Anzahl der Mitarbeiter), da für diese Berechnung die gesamte Mitarbeitertabelle durchsucht und dann nach Abteilungen gruppiert werden muss. Außerdem wird es einen kleinen (wir betrachten, dass es deutlich mehr Mitarbeiter als Abteilungen gibt) Zusatz je nach gewähltem Plan geben. O(log Anzahl der Mitarbeiter) oder O(Anzahl der Abteilungen) für die Gruppierung und ähnliches.

Es ist klar, dass die Aufwände für die Ausführung in verschiedenen DBMS unterschiedlich sein können, aber die Komplexität wird sich nicht ändern.

In der vorgeschlagenen Implementierung wird die funktionale DBMS eine Unterabfrage bilden, die die benötigten Werte pro Abteilung berechnet und anschließend einen JOIN mit der Abteilungstabelle durchführt, um den Namen zu erhalten. Bei der Deklaration jeder Funktion besteht die Möglichkeit, einen speziellen Marker MATERIALIZED festzulegen. Das System erstellt automatisch das entsprechende Feld für jede solche Funktion. Wenn sich der Wert der Funktion ändert, wird auch der Wert des Feldes in derselben Transaktion geändert. Bei einem Aufruf dieser Funktion erfolgt der Zugriff bereits auf das vorkalkulierte Feld.

Insbesondere, wenn MATERIALIZED für Funktionen gesetzt wird Mitarbeiteranzahl und GesamtsummeGehalt, wird die Tabelle mit den Abteilungslisten um zwei Felder erweitert, in denen die Anzahl der Mitarbeiter und deren Gesamtgehalt gespeichert werden. Bei jeder Änderung der Mitarbeiter, deren Gehältern oder Zugehörigkeiten zu den Abteilungen wird das System automatisch die Werte dieser Felder aktualisieren. Die oben genannte Anfrage wird direkt auf diese Felder zugreifen und wird ausgeführt für O(Anzahl der Abteilungen).

Welche Einschränkungen gibt es? Nur eine: Eine solche Funktion muss eine endliche Anzahl von Eingabewerten haben, für die ihr Wert definiert ist. Andernfalls kann keine Tabelle erstellt werden, die all ihre Werte speichert, da es keine Tabelle mit einer unendlichen Anzahl von Zeilen geben kann.

Beispiel:

Mitarbeiteranzahl 'Anzahl der Mitarbeiter mit einem Gehalt > N' (Abteilung d, NUMERIC[10,2] N) = 
    GRUPPENSUMME Gehalt(Mitarbeiter e) WENN abteilung(e) = d UND gehalt(e) > N;

Diese Funktion ist für eine unendliche Anzahl von Werten der Zahl N definiert (z.B. jedes negative Wert). Daher kann für sie kein MATERIALIZED gesetzt werden. Dies ist also eine logische und keine technische Einschränkung (das bedeutet, dass wir es nicht implementieren konnten). Ansonsten gibt es keine Einschränkungen. Gruppierungen, Sortierungen, AND und OR, PARTITION, Rekursionen usw. können verwendet werden.

Zum Beispiel kann für die Aufgabe 2.2 im vorherigen Artikel MATERIALIZED für beide Funktionen gesetzt werden:

gekauft 'Gekauft' (Kunde c, Produkt p, GANZZAHL y) = 
    GRUPPE SUMME sum(Detail d) WENN 
        kunde(bestellung(d)) = c UND 
        produkt(d) = p UND 
        extractYear(date(order(d))) = y MATERIALIZED;
bewertung 'Bewertung' (Kunde c, Produkt p, GANZZAHL y) = 
    PARTITION SUM 1 ORDER DESC bought(c, p, y), p BY c, y MATERIALIZED;
WÄHLE kontaktName(Kunde c), name(Produkt p) WHERE bewertung(c, p, 1997) < 3;

Das System wird automatisch eine Tabelle mit Typ-Schlüsseln erstellen. Kunde, Produkt und INTEGER, wird zwei Felder hinzufügen und die Werte der Felder bei allen Änderungen aktualisieren. Bei weiteren Aufrufen dieser Funktionen wird kein Berechnungsprozess stattfinden, stattdessen werden die Werte aus den entsprechenden Feldern gelesen.

Mit diesem Mechanismus können wir beispielsweise in Anfragen auf Rekursionen (CTE) verzichten. Insbesondere betrachten wir Gruppen, die mithilfe einer Beziehung zwischen Kind/Elternteil einen Baum bilden (jede Gruppe hat einen Verweis auf ihren Elternteil):

Eltern = DATEN Gruppe (Gruppe);

In der funktionalen Datenbank kann die Logik der Rekursionen wie folgt definiert werden:

Ebene (Gruppenkind, Gruppeneltern) = REKURSION 1l WENN Kind eine Gruppe IST UND Eltern == Kind
                                                             SCHRITT 2l WENN Eltern == Eltern($parent);
isParent (Gruppenkind, Gruppeneltern) = WAHR WENN Ebene(Kind, Eltern) MATERIALISIERT;

Da für die Funktion isParent MATERIALIZED gesetzt wurde, wird eine Tabelle mit zwei Schlüsseln (Gruppen) erstellt, in der das Feld isParent nur dann wahr ist, wenn der erste Schlüssel ein Nachkomme des zweiten ist. Die Anzahl der Einträge in dieser Tabelle entspricht der Anzahl der Gruppen multipliziert mit der durchschnittlichen Tiefe des Baumes. Wenn es z.B. erforderlich ist, die Anzahl der Nachkommen einer bestimmten Gruppe zu zählen, kann auf diese Funktion zugegriffen werden:

childrenCount (Gruppe g) = GRUPPE SUMME 1 WENN isParent(Gruppe kind, g);

Es wird kein CTE in der SQL-Anfrage vorhanden sein. Stattdessen wird einfach ein GROUP BY verwendet.

Mit diesem Mechanismus lässt sich auch einfach eine Denormalisierung der Datenbank bei Bedarf durchführen:

KLASSE Order 'Bestellung';
datum 'Datum' = DATUM DATUM (Bestellung);

CLASS OrderDetail 'Bestellzeile';
order 'Bestellung' = DATA Order (OrderDetail);
date 'Datum' (OrderDetail d) = date(order(d)) MATERIALIZED INDEXED;

Bei dem Aufruf der Funktion date wird für die Bestellzeile aus der Tabelle der Bestellzeilen das Feld gelesen, für das ein Index vorhanden ist. Bei einer Änderung des Bestelldatums wird das System automatisch das denormalisierte Datum in der Zeile neu berechnen.

Vorteile

Warum ist dieser gesamte Mechanismus notwendig? In klassischen DBMS können Entwickler oder DBAs ohne Umformulierung der Abfragen nur die Indizes ändern, Statistiken festlegen und dem Abfrageplaner Hinweise geben, wie diese auszuführen sind (wobei HINTs nur in kommerziellen DBMS vorhanden sind). So sehr sie sich auch bemühen, sie werden die erste Abfrage im Artikel nicht ausführen können für O (Anzahl der Abteilungen) ohne Änderung der Abfragen und das Hinzufügen von Triggern. In dem vorgeschlagenen Schema kann man in der Entwicklungsphase nicht über die Datenstruktur und welche Aggregationen verwendet werden sollen nachdenken. Das kann alles während des Betriebs ganz einfach geändert werden.

In der Praxis sieht das folgendermaßen aus. Einige Personen entwickeln direkt die Logik basierend auf der gestellten Aufgabe. Sie haben keine Ahnung von Algorithmen und deren Komplexität, noch von Ausführungsplänen, noch von Joins oder einer anderen technischen Komponente. Diese Personen sind eher Business-Analysten als Entwickler. Danach geht alles in die Testphase oder in den Betrieb. Es wird ein Logging für langwierige Abfragen aktiviert. Wenn eine langwierige Abfrage entdeckt wird, entscheiden andere, technisch versiertere Personen (in der Regel DBAs), die MATERIALIZED an einer bestimmten Zwischenfunktion aktivieren. Dadurch wird das Schreiben etwas verlangsamt (da ein zusätzliches Feld in der Transaktion aktualisiert werden muss). Allerdings wird nicht nur diese Abfrage erheblich beschleunigt, sondern auch alle anderen, die diese Funktion nutzen. Die Entscheidung darüber, welche Funktion materialisiert werden soll, wird relativ einfach getroffen. Zwei Hauptparameter: die Anzahl möglicher Eingangswerte (genau so viele Datensätze werden in der entsprechenden Tabelle vorhanden sein) und wie oft sie in anderen Funktionen verwendet wird.

Analogien

In modernen kommerziellen DBMS gibt es ähnliche Mechanismen: MATERIALIZED VIEW mit FAST REFRESH (Oracle) und INDEXED VIEW (Microsoft SQL Server). In PostgreSQL kann MATERIALIZED VIEW nur auf Anfrage und unter sehr strengen Einschränkungen aktualisiert werden, sodass wir es nicht betrachten. Aber sie haben einige Probleme, die ihre Nutzung erheblich einschränken.

Erstens kann die Materialisierung nur aktiviert werden, wenn bereits eine normale VIEW erstellt wurde. Andernfalls muss man die anderen Abfragen umschreiben, um auf die neu erstellte Ansicht zuzugreifen, damit man diese Materialisierung nutzen kann. Oder man lässt alles wie es ist, aber das wäre mindestens ineffektiv, wenn Daten bereits vorausberechnet sind, die viele Abfragen jedoch nicht immer nutzen und neu berechnen.

Zweitens gibt es eine enorme Anzahl an Einschränkungen:

Oracle

5.3.8.4 Allgemeine Einschränkungen für Fast Refresh

Die definierende Abfrage der materialisierten Ansicht unterliegt folgenden Einschränkungen:

  • Die materialisierte Ansicht darf keine Verweise auf nicht wiederholende Ausdrücke wie SYSDATE and ROWNUM.
  • Die materialisierte Ansicht darf keine Verweise auf RAW or LONG RAW Datentypen enthalten.
  • Sie darf kein SELECT Unterabfrage.
  • Sie darf keine analytischen Funktionen (zum Beispiel, RANK) in der SELECT Klausel haben.
  • Sie darf keine Tabelle referenzieren, für die ein XMLIndex Index definiert ist.
  • Sie darf kein MODEL Klausel haben.
  • Sie darf kein HAVING Klausel mit einer Unterabfrage.
  • Sie darf keine geschachtelten Abfragen enthalten, die ANY, ALL, oder NOT EXISTS.
  • Sie darf kein [START WITH …] CONNECT BY Klausel haben.
  • Sie darf keine mehreren Detailtabellen an unterschiedlichen Standorten enthalten.
  • AN COMMIT Materialisierte Ansichten dürfen keine externen Detailtabellen haben.
  • Geschachtelte materialisierte Ansichten müssen einen Join oder eine Aggregation haben.
  • Materialisierte Join-Ansichten und materialisierte Aggregatansichten mit einer GROUP BY Klausel dürfen nicht von einer index-organisierten Tabelle auswählen.

5.3.8.5 Einschränkungen für Fast Refresh bei nur Materialisierten Ansichten mit Joins

Definierende Abfragen für materialisierte Ansichten mit nur Joins und ohne Aggregationen haben folgende Einschränkungen für Fast Refresh:

  • Alle Einschränkungen aus „Allgemeinen Einschränkungen für Fast Refresh«.
  • Sie dürfen keine GROUP BY Klauseln oder Aggregationen haben.
  • Rowids aller Tabellen in der FROM Liste müssen in der SELECT Abfrage erscheinen.
  • Materialisierte Ansicht Protokolle müssen mit Rowids für alle Basistabellen in der FROM Abfrage erscheinen.
  • Sie können keine schnell aktualisierbare materialisierte Ansicht aus mehreren Tabellen mit einfachen Joins erstellen, die eine Objekttyp-Spalte in der SELECT Anweisung enthalten.

Außerdem ist die von Ihnen gewählte Aktualisierungsmethode nicht optimal effizient, wenn:

  • Die definierende Abfrage einen äußeren Join verwendet, der sich wie ein innerer Join verhält. Wenn die definierende Abfrage einen solchen Join enthält, ziehen Sie in Betracht, die definierende Abfrage so umzuschreiben, dass sie einen inneren Join enthält.
  • Der SELECT Die Liste der materialisierten Ansicht enthält Ausdrücke zu Spalten aus mehreren Tabellen.

5.3.8.6 Einschränkungen für Fast Refresh bei Materialisierten Ansichten mit Aggregationen

Definierende Abfragen für materialisierte Ansichten mit Aggregationen oder Joins haben folgende Einschränkungen für Fast Refresh:

Fast Refresh wird für beide unterstützt AN COMMIT and AN DEMAND materialisierte Ansichten, jedoch gelten folgende Einschränkungen:

  • Alle Tabellen in der materialisierten Ansicht müssen Materialisierte Ansicht Protokolle haben, und die Materialisierte Ansicht Protokolle müssen:
    • Alle Spalten aus der Tabelle, die in der materialisierten Ansicht referenziert wird, enthalten.
    • Spezifizieren Sie mit ROWID and EINSCHLIESSLICH NEU VALUES.
    • Geben Sie die SEQUENZ Klausel an, wenn die Tabelle mit einer Mischung aus Einsätzen/Direct-Loads, Löschungen und Aktualisierungen rechnen muss.

  • Nur SUM, COUNT, AVG, STDDEV, VARIANCE, MIN and MAX werden für Fast Refresh unterstützt.
  • COUNT(*) muss angegeben werden.
  • Aggregatfunktionen dürfen nur als äußerster Teil des Ausdrucks auftreten. Das heißt, Aggregationen wie AVG(AVG(x)) or AVG(x)+ AVG(x) sind nicht erlaubt.
  • Für jedes Aggregat wie AVG(expr), muss das entsprechende COUNT(expr) vorhanden sein. Oracle empfiehlt, dass SUM(expr) angegeben wird.
  • Wenn VARIANCE(expr) or STDDEV(expr) angegeben ist, COUNT(expr) and SUM(expr) muss angegeben werden. Oracle empfiehlt, dass SUM(expr *expr) angegeben wird.
  • Der SELECT die Spalte in der definierenden Abfrage kein komplexer Ausdruck mit Spalten aus mehreren Basistabellen sein kann. Ein möglicher Workaround hierfür ist die Verwendung einer geschachtelten materialisierten Sicht.
  • Der SELECT Die Liste muss alle GROUP BY Spalten enthalten.
  • Die materialisierte Sicht basiert nicht auf einer oder mehreren entfernten Tabellen.
  • Wenn Sie einen CHAR Datentyp in den Filterspalten eines Protokolls für materialisierte Sichten verwenden, müssen die Zeichencodierungen der Mastereinsatzstelle und der materialisierten Sicht identisch sein.
  • Wenn die materialisierte Sicht eines der folgenden Merkmale aufweist, wird eine schnelle Auffrischung nur bei konventionellen DML-Einfügen und direkten Ladevorgängen unterstützt.
    • Materialisierte Sichten mit MIN or MAX Aggregaten
    • Materialisierte Sichten, die SUM(expr) aber keine COUNT(expr)
    • Materialisierte Sichten ohne COUNT(*)

    Eine solche materialisierte Sicht wird als einfüge-only materialisierte Sicht bezeichnet.

  • Eine materialisierte Sicht mit MAX or MIN kann nach Lösch- oder gemischten DML-Anweisungen schnell aktualisiert werden, wenn sie kein WHERE Klausel haben.
    Die max/min schnelle Aktualisierung nach Lösch- oder gemischten DML hat nicht das gleiche Verhalten wie der einfüge-only Fall. Sie löscht und berechnet die max/min Werte für die betroffenen Gruppen erneut. Sie sollten sich der Leistungsauswirkungen bewusst sein.
  • Materialisierte Sichten mit benannten Sichten oder Unterabfragen in der FROM Klausel können schnell aktualisiert werden, sofern die Sichten vollständig zusammengeführt werden können. Weitere Informationen dazu, welche Sichten zusammengeführt werden, finden Sie in Oracle Database SQL Language Reference.
  • Wenn keine äußeren Joins vorhanden sind, können Sie beliebige Auswahlen und Joins in der WHERE Klausel haben.
  • Materialisierte aggregierte Sichten mit äußeren Joins sind nach konventionellem DML und direkten Ladevorgängen schnell aktualisierbar, vorausgesetzt, dass nur die äußere Tabelle geändert wurde. Außerdem müssen eindeutige Einschränkungen auf den Join-Spalten der innneren Join-Tabelle existieren. Wenn äußere Joins vorhanden sind, müssen alle Joins durch ANDverbunden sein und den Gleichheits- (=) Operator verwenden.
  • Für materialisierte Sichten mit CUBE, ROLLUP, Gruppierungssätzen oder deren Verknüpfungen gelten die folgenden Einschränkungen:
    • Der SELECT Die Liste sollte ein Gruppierungsunterscheidungsmerkmal enthalten, das entweder ein GROUPING_ID Funktion auf allen GROUP BY Ausdrücken oder GROUPING Funktionen, je eine für jeden GROUP BY Ausdruck. Zum Beispiel, wenn die GROUP BY Klausel der materialisierten Sicht „GROUP BY CUBE(a, b)“ ist, sollte die SELECT Liste entweder „GROUPING_ID(a, b)“ oder „GROUPING(a) AND GROUPING(b)“ enthalten, damit die materialisierte Sicht schnell aktualisiert werden kann.
    • GROUP BY sollte keine doppelten Gruppierungen erzeugen. Zum Beispiel, „GROUP BY a, ROLLUP(a, b)“ ist nicht schnell aktualisierbar, da es zu doppelten Gruppierungen „(a), (a, b), UND (a)«.

5.3.8.7 Einschränkungen für schnelle Aktualisierungen von materialisierten Sichten mit UNION ALL

Materialisierte Sichten mit dem UNION ALL Mengenoperator unterstützen die REFRESH FAST Option, wenn die folgenden Bedingungen erfüllt sind:

  • Die definierende Abfrage muss den UNION ALL Operator auf der obersten Ebene haben.

    Der UNION ALL Der Operator darf nicht in einer Unterabfrage eingebettet sein, mit einer Ausnahme: Der UNION ALL kann in einer Unterabfrage in der FROM Klausel erfolgen, vorausgesetzt, die definierende Abfrage hat die Form SELECT * FROM (Sicht oder Unterabfrage mit UNION ALL) wie im folgenden Beispiel:

    CREATE VIEW view_with_unionall AS
    (SELECT c.rowid crid, c.cust_id, 2 umarker
     FROM customers c WHERE c.cust_last_name = 'Smith'
     UNION ALL
     SELECT c.rowid crid, c.cust_id, 3 umarker
     FROM customers c WHERE c.cust_last_name = 'Jones');
    
    CREATE MATERIALIZED VIEW unionall_inside_view_mv
    REFRESH FAST ON DEMAND AS
    SELECT * FROM view_with_unionall;
    

    Beachten Sie, dass die Sicht view_with_unionall die Anforderungen für eine schnelle Aktualisierung erfüllt.

  • Jeder Abfrageblock in der UNION ALL Abfrage muss die Anforderungen einer schnell aktualisierbaren materialisierten Sicht mit Aggregaten oder einer schnell aktualisierbaren materialisierten Sicht mit Joins erfüllen.

    Die entsprechenden Protokolle für materialisierte Sichten müssen auf den Tabellen erstellt werden, wie für den entsprechenden Typ einer schnell aktualisierbaren materialisierten Sicht erforderlich.
    Beachten Sie, dass die Oracle-Datenbank auch den Sonderfall einer einzelnen materialisierten Ansicht mit Joins erlaubt, vorausgesetzt, dass die ROWID Spalte in der SELECT Liste und im Protokoll der materialisierten Sicht enthalten ist. Dies wird in der definierenden Abfrage der Ansicht gezeigt view_with_unionall.

  • Der SELECT Die Liste jeder Abfrage muss ein UNION ALL Marker enthalten, und die UNION ALL Spalte muss in jedem UNION ALL Zweig einen eindeutigen konstanten numerischen oder string Wert haben. Außerdem muss die Marker-Spalte in der gleichen ordinalen Position in der SELECT Liste jedes Abfrageblocks erscheinen. Siehe „UNION ALL Marker und Abfrage-Neuschreibung“ für weitere Informationen zu UNION ALL Markern.
  • Einige Funktionen wie äußere Joins, nur einfügbare aggregierte materialisierte Sichtabfragen und entfernte Tabellen werden für materialisierte Sichten mit UNION ALLnicht unterstützt. Beachten Sie jedoch, dass materialisierte Sichten, die in der Replikation verwendet werden und keine Joins oder Aggregate enthalten, schnell aktualisiert werden können, wenn UNION ALL oder entfernte Tabellen verwendet werden.
  • Der Kompatibilitätsinitialisierungsparameter muss auf 9.2.0 oder höher eingestellt sein, um eine schnell aktualisierbare materialisierte Sicht mit UNION ALL.

Ich möchte die Oracle-Anhänger nicht beleidigen, aber nach ihrer Liste von Einschränkungen scheint es, als ob dieser Mechanismus nicht allgemein verfasst wurde, wobei ein Modell verwendet wurde, sondern von Tausenden von Indern, wo jeder seine eigene Zweig erstellte und jeder von ihnen tat, was er konnte. Die Verwendung dieses Mechanismus für echte Logik ist wie das Gehen über ein Minenfeld. Man kann jederzeit auf eine Mine stoßen und auf eines der nicht offensichtlichen Einschränkungen treffen. Wie das funktioniert, ist auch eine eigene Frage, aber sie liegt außerhalb des Rahmens dieses Artikels.

Microsoft SQL Server

Zusätzliche Anforderungen

Neben den SET-Optionen und Anforderungen an deterministische Funktionen müssen die folgenden Anforderungen erfüllt sein:

  • Der Benutzer, der CREATE INDEX ausführt, muss der Eigentümer der Ansicht sein.
  • Beim Erstellen des Index muss die IGNORE_DUP_KEY Option auf AUS (die Standardeinstellung) gesetzt werden.
  • Tabellen müssen mit zwei Teilen benannt werden, schema.tablename in der Definition der Ansicht.
  • Benutzerdefinierte Funktionen, die in der Ansicht verwendet werden, müssen mit der WITH SCHEMABINDING Option erstellt werden.
  • Alle benutzerdefinierten Funktionen, die in der Ansicht verwendet werden, müssen mit zwei Teilen benannt werden, <schema>.<function>.
  • Die Datenzugriffs-Eigenschaft einer benutzerdefinierten Funktion muss sein NO SQL, und die externe Zugriffs-Eigenschaft muss NEIN.
  • Gemeinsame Sprachlaufzeit (CLR)-Funktionen können in der Selektionsliste der Ansicht erscheinen, dürfen jedoch nicht Teil der Definition des Schüssel der gruppierten Indizes sein. CLR-Funktionen dürfen nicht in der WHERE-Klausel der Ansicht oder der ON-Klausel einer JOIN-Operation in der Ansicht erscheinen.
  • Die CLR-Funktionen und Methoden der benutzerdefinierten CLR-Typen, die in der Definition der Ansicht verwendet werden, müssen die Eigenschaften haben, die in der folgenden Tabelle gezeigt sind.

    Eigenschaft
    Note

    DETERMINISTISCH = WAHR
    Muss ausdrücklich als Attribut der Microsoft .NET Framework-Methode deklariert werden.

    GENAU = WAHR
    Muss ausdrücklich als Attribut der .NET Framework-Methode deklariert werden.

    DATENZUGRIFF = NO SQL
    Bestimmt durch Einstellung des DataAccess-Attributs auf DataAccessKind.None und des SystemDataAccess-Attributs auf SystemDataAccessKind.None.

    EXTERNER ZUGRIFF = NEIN
    Dieses Attribut hat standardmäßig den Wert NEIN für CLR-Routinen.

  • Die Ansicht muss unter Verwendung der WITH SCHEMABINDING Option erstellt werden.
  • Die Ansicht darf nur Basis-Tabellen referenzieren, die sich in der gleichen Datenbank wie die Ansicht befinden. Die Ansicht darf keine anderen Ansichten referenzieren.
  • Die SELECT-Anweisung in der Definition der Ansicht darf die folgenden Transact-SQL-Elemente nicht enthalten:

    COUNT
    ROWSET-Funktionen (OPENDATASOURCE, OPENQUERY, OPENROWSET, UND OPENXML)
    ÄUßERE Joins (LINKS, RECHTS, oder FULL)

    Abgeleitete Tabelle (definiert durch Angabe einer SELECT Anweisung in der FROM Klausel)
    Selbst-joins
    Spalten durch Angabe von SELECT * or SELECT

    .*

    DISTINCT
    STDEV, STDEVP, VAR, VARP, oder AVG
    Gemeinsame Tabellenausdrücke (CTE)

    float1, text, ntext, Image, XML, oder filestream Spalten
    Unterabfrage
    ÜBER Klausel, die Ranking- oder aggregierte Fensterfunktionen umfasst

    Volltext-Prädikate (ENTHÄLT, FREETEXT)
    SUM Funktion, die auf einen nullfähigen Ausdruck verweist
    , der beim Erstellen der Tabelle angegeben wurde, sortiert.

    CLR benutzerdefinierte aggregierte Funktion
    OBEN
    CUBE, ROLLUP, oder GRUPPIERUNGSMENGEN Operatoren

    MIN, MAX
    UNION, AUSSER, oder INTERSECT Operatoren
    TABELLENPROBE

    Tabellenvariablen
    ÄUßERE ANWENDUNG or CROSS APPLY
    DREHEN, ENTDREHEN

    Sparse-Säulensätze
    Inline (TVF) oder mehrstellige tabellenwertige Funktionen (MSTVF)
    OFFSET

    CHECKSUM_AGG

    1 Die indizierte Ansicht kann enthalten float Spalten; jedoch können solche Spalten nicht im Schlüssel des gruppierten Index enthalten sein.

  • Wenn GROUP BY ist vorhanden, muss die Sichtdefinition enthalten COUNT_BIG(*) und darf nicht enthalten HAVING. Diese GROUP BY Einschränkungen gelten nur für die Definition der indizierten Ansicht. Eine Abfrage kann eine indizierte Ansicht in ihrem Ausführungsplan verwenden, auch wenn sie diese GROUP BY Einschränkungen nicht erfüllt.
  • Wenn die Sichtdefinition eine GROUP BY Klausel enthält, kann der Schlüssel des eindeutigen gruppierten Index nur auf die in der GROUP BY Klausel haben.
  • Hier sieht man, dass Inder nicht angezogen wurden, da sie beschlossen haben, nach dem Schema "wenig machen, aber gut" zu arbeiten. Das bedeutet, dass sie zwar mehr Minen auf dem Feld haben, aber deren Anordnung transparenter ist. Am enttäuschendsten ist dieses Einschränkung:

    Die Ansicht darf nur Basis-Tabellen referenzieren, die sich in der gleichen Datenbank wie die Ansicht befinden. Die Ansicht darf keine anderen Ansichten referenzieren.

    In unserer Terminologie bedeutet dies, dass eine Funktion nicht auf eine andere materialisierte Funktion zugreifen kann. Das schneidet die gesamte Ideologie im Keim ab.
    Außerdem reduziert dieses Einschränkung (und weiter im Text) die Nutzungsmöglichkeiten erheblich:

    Die SELECT-Anweisung in der Definition der Ansicht darf die folgenden Transact-SQL-Elemente nicht enthalten:

    COUNT
    ROWSET-Funktionen (OPENDATASOURCE, OPENQUERY, OPENROWSET, UND OPENXML)
    ÄUßERE Joins (LINKS, RECHTS, oder FULL)

    Abgeleitete Tabelle (definiert durch Angabe einer SELECT Anweisung in der FROM Klausel)
    Selbst-joins
    Spalten durch Angabe von SELECT * or SELECT

    .*

    DISTINCT
    STDEV, STDEVP, VAR, VARP, oder AVG
    Gemeinsame Tabellenausdrücke (CTE)

    float1, text, ntext, Image, XML, oder filestream Spalten
    Unterabfrage
    ÜBER Klausel, die Ranking- oder aggregierte Fensterfunktionen umfasst

    Volltext-Prädikate (ENTHÄLT, FREETEXT)
    SUM Funktion, die auf einen nullfähigen Ausdruck verweist
    , der beim Erstellen der Tabelle angegeben wurde, sortiert.

    CLR benutzerdefinierte aggregierte Funktion
    OBEN
    CUBE, ROLLUP, oder GRUPPIERUNGSMENGEN Operatoren

    MIN, MAX
    UNION, AUSSER, oder INTERSECT Operatoren
    TABELLENPROBE

    Tabellenvariablen
    ÄUßERE ANWENDUNG or CROSS APPLY
    DREHEN, ENTDREHEN

    Sparse-Säulensätze
    Inline (TVF) oder mehrstellige tabellenwertige Funktionen (MSTVF)
    OFFSET

    CHECKSUM_AGG

    OUTER JOINS, UNION, ORDER BY und anderes sind verboten. Vielleicht wäre es einfacher gewesen, anzugeben, was verwendet werden kann, als das, was nicht kann. Die Liste wäre wahrscheinlich viel kürzer gewesen.

    Zusammenfassend: ein riesiges Set von Einschränkungen in jeder (ich möchte anmerken, kommerziellen) DBMS vs. keine (außer einer logischen, nicht technischen) in der LGPL-Technologie. Es sollte jedoch beachtet werden, dass die Implementierung dieses Mechanismus in relationaler Logik etwas komplizierter ist als in der beschriebenen Funktionalität.

    Implementierung

    Wie funktioniert das? Als "virtuelle Maschine" wird PostgreSQL verwendet. Innen gibt es einen komplexen Algorithmus, der mit dem Aufbau von Abfragen beschäftigt ist. Hier den Quellcode. Und dort gibt es nicht nur eine große Menge an Heuristiken mit einer Menge von if-Anweisungen. Wenn man also ein paar Monate Zeit hat zum Lernen, kann man versuchen, sich in die Architektur einzuarbeiten.

    Funktioniert das effektiv? Ja, es funktioniert ziemlich gut. Leider ist es schwer, das zu beweisen. Ich kann nur sagen, dass, wenn man sich die Tausenden von Anfragen ansieht, die in großen Anwendungen vorkommen, sie im Durchschnitt effektiver sind als die eines guten Entwicklers. Ein ausgezeichneter SQL-Programmierer kann jede Anfrage effektiver schreiben, doch bei Tausend Anfragen fehlt ihm einfach die Motivation und die Zeit, dies zu tun. Das Einzige, was ich jetzt als Nachweis für die Effizienz anführen kann, ist, dass auf Basis der Plattform, die auf diesem DBMS aufgebaut ist, mehrere Projekte laufen. ERP-Systeme, in denen es Tausende von verschiedenen MATERIALIZED-Funktionen gibt, mit Tausenden von Benutzern und Terabyte von Datenbanken mit Hunderten von Millionen von Datensätzen, die auf einem normalen Dual-Prozessor-Server betrieben werden. Jeder kann jedoch die Effizienz prüfen oder widerlegen, indem er plattforms und PostgreSQL, die Unterstützung aktiviert wurde SQL-Abfragen protokolliert und versucht, die Logik und die Daten dort zu ändern.

    In den kommenden Artikeln werde ich auch darüber berichten, wie man Einschränkungen für Funktionen, den Umgang mit Änderungs-Sitzungen und vieles mehr festlegen kann.

    Quelle: habr.com

    60GB SSD 8Gb DDR4