Wie kann SQL über eine Unterabfrage gelöscht werden

15

Der folgende Code wurde von einem unserer Entwickler hinzugefügt, um doppelte Datensätze aus der Tabelle zu löschen:

DELETE  SubQuery

FROM
(
    SELECT  ID
            ,FK1
            ,FK2
            ,CreatedDateTime
            ,ROW_NUMBER() OVER(PARTITION BY FK1, FK2 ORDER BY CreatedDateTime) AS RowNumber

    FROM    Table
)
AS SubQuery

WHERE   RowNumber > 1

Bei der Überprüfung des Codes bin ich davon ausgegangen, dass er nicht funktioniert. Das Testen in unserer Testumgebung (SQL 2014) zeigt jedoch, dass dies der Fall ist!

Wie kann SQL die Unterabfrage auflösen und die Datensätze von löschen table?

Greg
quelle

Antworten:

14

Das, was subquerySie in Ihrem Code haben, wird als abgeleitete Tabelle bezeichnet . Es ist keine Basistabelle, sondern eine Tabelle, die während der Ausführung der Abfrage "lebt". Wie Sichten (die auch als angezeigte Tabellen bezeichnet werden ) - und in neueren Versionen CTEs, die eine weitere, vierte Möglichkeit zum "Definieren" einer Tabelle in einer Abfrage darstellen - ähneln sie in vielerlei Hinsicht einer Tabelle. Sie können selectvon ihnen, Sie können sie in fromoder joinzu anderen Tabellen (Basis oder nicht!) Verwenden .

In einigen DBMS (nicht alle DBMS haben dies auf die gleiche Weise implementiert) können diese Tabellen / Ansichten aktualisiert werden . Und „aktualisierbar“ bedeutet , dass wir auch update, insertin oder deletevon ihnen.

Es gibt jedoch Einschränkungen und dies wird erwartet. Stellen Sie sich vor, das wäre subqueryein Join von 2 (oder 17 Tabellen). Was würde deletedann bedeuten? (Aus welchen Tabellen sollten Zeilen gelöscht werden?) Aktualisierbare Ansichten sind eine sehr komplizierte Angelegenheit . Es gibt ein aktuelles (2012) Buch, das sich ausschließlich mit diesem Thema befasst und von Chris Date, einem bekannten Experten für relationale Theorie, geschrieben wurde: View Updating and Relational Theory .

Wenn die abgeleitete Tabelle (oder Sicht) eine sehr einfache Abfrage ist, wie es nur eine Basistabelle (möglicherweise eingeschränkt durch a WHERE) und nein hat GROUP BY, dann entspricht jede Zeile der abgeleiteten Tabelle einer Zeile in der zugrunde liegenden Basistabelle, so ist es einfach * zu aktualisieren, einfügen oder daraus löschen.

Wenn der Code in der Unterabfrage komplexer ist, hängt es davon ab, ob die Zeilen der abgeleiteten Tabelle / Sicht auf Zeilen aus einer der zugrunde liegenden Basistabellen zurückverfolgt / aufgelöst werden können.

Für SQL Server, können Sie mehr in dem aktualisierbaren Views Absatz in MSDN lesen: CREATE VIEW.

Aktualisierbare Ansichten

Sie können die Daten einer zugrunde liegenden Basistabelle über eine Ansicht ändern, sofern die folgenden Bedingungen erfüllt sind:

  • Alle Änderungen, einschließlich UPDATE, INSERTund DELETEAussagen, muss Referenzspalten von nur einer Basistabelle.

  • Die in der Ansicht geänderten Spalten müssen direkt auf die zugrunde liegenden Daten in den Tabellenspalten verweisen. Die Spalten können nicht auf andere Weise abgeleitet werden, z. B. durch Folgendes:

  • Eine Aggregatfunktion: AVG, COUNT, SUM, MIN, MAX, GROUPING, STDEV, STDEVP, VAR, und VARP.

  • Eine Berechnung. Die Spalte kann nicht aus einem Ausdruck berechnet werden, der andere Spalten verwendet. Spalten, die unter Verwendung der eingestellten Operatoren gebildet werden UNION, UNION ALL, CROSSJOIN, EXCEPT, und INTERSECT belaufen sich auf eine Berechnung und sind auch nicht aktualisierbar.

  • Die Spalten geändert werden , werden durch nicht berührt GROUP BY, HAVINGoder DISTINCTKlauseln.

  • TOPwird nirgendwo in der select_statement der Ansicht zusammen mit der WITH CHECK OPTIONKlausel verwendet.

Die vorherigen Einschränkungen gelten für alle Unterabfragen in der FROMKlausel der Ansicht genauso wie für die Ansicht selbst. Im Allgemeinen muss das Datenbankmodul in der Lage sein, Änderungen von der Sichtdefinition bis zu einer Basistabelle eindeutig nachzuverfolgen.


Eigentlich deleteist einfacher, weniger komplex als update. SQL Server benötigt nur die Primärschlüssel oder eine andere Methode, um zu ermitteln, welche Zeilen der Basistabelle gelöscht werden sollen. Denn updatees gibt eine zusätzliche (ziemlich offensichtliche) Einschränkung, dass wir eine berechnete Spalte nicht aktualisieren können. Sie können versuchen, Ihre Abfrage zu ändern, um ein Update durchzuführen. Das Aktualisieren der CreatedDateTimewird wahrscheinlich gut funktionieren, aber der Versuch, die berechnete RowNumberSpalte zu aktualisieren, wird einen Fehler auslösen. Und insertist noch komplexer, da wir Werte für alle Spalten der Basistabelle bereitstellen müssten, für die es keine DEFAULTEinschränkung gibt.

ypercubeᵀᴹ
quelle
4

Es ist leicht zu erkennen, wenn Sie sich den Abfrageplan ansehen. In Ihrem Fall enthält der Plan nur einen zusätzlichen Operator für Segment- und Sequenzprojekte, der die Zeilennummer verwaltet. Diese Art von Vorgang funktioniert nur, wenn SQL Server die zugrunde liegende Tabelle tatsächlich auflösen kann.

Das Löschen aus Unterabfragen und CTEs wird vollständig unterstützt und ist sehr effizient, insbesondere zum Entfernen von Duplikaten. Ich scheine mich auch zu erinnern, es auf älteren Versionen von SQL Server verwendet zu haben.

Mehr in einem alten Blogbeitrag von mir.

Daniel Hutmacher
quelle