Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Ist SQL Server DRI (ON DELETE CASCADE) langsam?

SQL Server eignet sich am besten für satzbasierte Operationen, während CASCADE Löschungen sind naturgemäß datensatzbasiert.

SQL Server versucht im Gegensatz zu den anderen Servern, die unmittelbaren satzbasierten Operationen zu optimieren, funktioniert jedoch nur eine Ebene tief. Es müssen die Datensätze in den Tabellen der oberen Ebene gelöscht werden, um die Datensätze in den Tabellen der unteren Ebene zu löschen.

Mit anderen Worten, kaskadierende Operationen arbeiten von oben nach unten, während Ihre Lösung von unten nach oben arbeitet, was satzbasierter und effizienter ist.

Hier ist ein Beispielschema:

CREATE TABLE t_g (id INT NOT NULL PRIMARY KEY)

CREATE TABLE t_p (id INT NOT NULL PRIMARY KEY, g INT NOT NULL, CONSTRAINT fk_p_g FOREIGN KEY (g) REFERENCES t_g ON DELETE CASCADE)

CREATE TABLE t_c (id INT NOT NULL PRIMARY KEY, p INT NOT NULL, CONSTRAINT fk_c_p FOREIGN KEY (p) REFERENCES t_p ON DELETE CASCADE)

CREATE INDEX ix_p_g ON t_p (g)

CREATE INDEX ix_c_p ON t_c (p)

, diese Abfrage:

DELETE
FROM    t_g
WHERE   id > 50000

und sein Plan:

  |--Sequence
       |--Table Spool
       |    |--Clustered Index Delete(OBJECT:([test].[dbo].[t_g].[PK__t_g__176E4C6B]), WHERE:([test].[dbo].[t_g].[id] > (50000)))
       |--Index Delete(OBJECT:([test].[dbo].[t_p].[ix_p_g]) WITH ORDERED PREFETCH)
       |    |--Sort(ORDER BY:([test].[dbo].[t_p].[g] ASC, [test].[dbo].[t_p].[id] ASC))
       |         |--Table Spool
       |              |--Clustered Index Delete(OBJECT:([test].[dbo].[t_p].[PK__t_p__195694DD]) WITH ORDERED PREFETCH)
       |                   |--Sort(ORDER BY:([test].[dbo].[t_p].[id] ASC))
       |                        |--Merge Join(Inner Join, MERGE:([test].[dbo].[t_g].[id])=([test].[dbo].[t_p].[g]), RESIDUAL:([test].[dbo].[t_p].[g]=[test].[dbo].[t_g].[id]))
       |                             |--Table Spool
       |                             |--Index Scan(OBJECT:([test].[dbo].[t_p].[ix_p_g]), ORDERED FORWARD)
       |--Index Delete(OBJECT:([test].[dbo].[t_c].[ix_c_p]) WITH ORDERED PREFETCH)
            |--Sort(ORDER BY:([test].[dbo].[t_c].[p] ASC, [test].[dbo].[t_c].[id] ASC))
                 |--Clustered Index Delete(OBJECT:([test].[dbo].[t_c].[PK__t_c__1C330188]) WITH ORDERED PREFETCH)
                      |--Table Spool
                           |--Sort(ORDER BY:([test].[dbo].[t_c].[id] ASC))
                                |--Hash Match(Inner Join, HASH:([test].[dbo].[t_p].[id])=([test].[dbo].[t_c].[p]))
                                     |--Table Spool
                                     |--Index Scan(OBJECT:([test].[dbo].[t_c].[ix_c_p]), ORDERED FORWARD)

Zuerst SQL Server löscht Datensätze aus t_g , fügt dann die gelöschten Datensätze mit t_p hinzu und löscht aus letzterem, verbindet schließlich Datensätze, die aus t_p gelöscht wurden mit t_c und löscht aus t_c .

Ein einzelner Drei-Tabellen-Join wäre in diesem Fall viel effizienter, und genau das machen Sie mit Ihrer Problemumgehung.

Wenn Sie sich dadurch besser fühlen, Oracle optimiert in keiner Weise Kaskadenoperationen:Sie sind immer NESTED LOOPS und Gott helfe Ihnen, wenn Sie vergessen haben, einen Index für die Referenzierungsspalte zu erstellen.