Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

SQL Server DRI (ON DELETE CASCADE) è lento?

SQL Server è il migliore nelle operazioni basate su insiemi, mentre CASCADE le eliminazioni sono, per loro natura, basate su record.

SQL Server , a differenza degli altri server, cerca di ottimizzare le operazioni immediate basate sui set, tuttavia funziona solo a un livello di profondità. È necessario che i record vengano eliminati nelle tabelle di livello superiore per eliminare quelli nelle tabelle di livello inferiore.

In altre parole, le operazioni a cascata funzionano dall'alto verso il basso, mentre la tua soluzione funziona dall'alto verso il basso, che è più basata su set ed efficiente.

Ecco uno schema di esempio:

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)

, questa domanda:

DELETE
FROM    t_g
WHERE   id > 50000

e il suo piano:

  |--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)

Innanzitutto, SQL Server elimina i record da t_g , quindi unisce i record eliminati con t_p e cancella da quest'ultimo, infine, unisce i record cancellati da t_p con t_c ed elimina da t_c .

Un unico join a tre tabelle sarebbe molto più efficiente in questo caso, e questo è ciò che fai con la tua soluzione alternativa.

Se ti fa sentire meglio, Oracle non ottimizza in alcun modo le operazioni a cascata:sono sempre NESTED LOOPS e che Dio ti aiuti se ti sei dimenticato di creare un indice sulla colonna di riferimento.