INSERIMENTO efficiente in una tabella con indice cluster


28

Ho un'istruzione SQL che inserisce le righe in una tabella con un indice cluster sulla colonna TRACKING_NUMBER.

PER ESEMPIO:

INSERT INTO TABL_NAME (TRACKING_NUMBER, COLB, COLC) 
SELECT TRACKING_NUMBER, COL_B, COL_C 
FROM STAGING_TABLE

La mia domanda è: aiuta a utilizzare una clausola ORDER BY nell'istruzione SELECT per la colonna dell'indice cluster, o qualsiasi guadagno ottenuto sarebbe negato dall'ordinamento aggiuntivo richiesto per la clausola ORDER BY?

Risposte:


18

Come già indicato dalle altre risposte, SQL Server può o meno garantire esplicitamente che le righe siano ordinate in ordine di indice cluster prima di insert.

Ciò dipende dal fatto che l'operatore dell'indice cluster nel piano abbia DMLRequestSortimpostato la proprietà (che a sua volta dipende dal numero stimato di righe che vengono inserite).

Se ritieni che SQL Server lo stia sottovalutando per qualsiasi motivo potresti trarre vantaggio dall'aggiunta di un esplicito ORDER BYalla SELECTquery per ridurre al minimo le divisioni di pagina e la conseguente frammentazione INSERTdall'operazione

Esempio:

use tempdb;

GO

CREATE TABLE T(N INT PRIMARY KEY,Filler char(2000))

CREATE TABLE T2(N INT PRIMARY KEY,Filler char(2000))

GO

DECLARE @T TABLE (U UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),N int)

INSERT INTO @T(N)
SELECT number 
FROM master..spt_values
WHERE type = 'P' AND number BETWEEN 0 AND 499

/*Estimated row count wrong as inserting from table variable*/
INSERT INTO T(N)
SELECT T1.N*1000 + T2.N
FROM @T T1, @T T2

/*Same operation using explicit sort*/    
INSERT INTO T2(N)
SELECT T1.N*1000 + T2.N
FROM @T T1, @T T2
ORDER BY T1.N*1000 + T2.N


SELECT avg_fragmentation_in_percent,
       fragment_count,
       page_count,
       avg_page_space_used_in_percent,
       record_count
FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T'), NULL, NULL, 'DETAILED')
;  


SELECT avg_fragmentation_in_percent,
       fragment_count,
       page_count,
       avg_page_space_used_in_percent,
       record_count
FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T2'), NULL, NULL, 'DETAILED')
;  

Mostra che Tè fortemente frammentato

avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
---------------------------- -------------------- -------------------- ------------------------------ --------------------
99.3116118225536             92535                92535                67.1668272794663               250000
99.5                         200                  200                  74.2868173956017               92535
0                            1                    1                    32.0978502594514               200

Ma per la T2frammentazione è minimo

avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
---------------------------- -------------------- -------------------- ------------------------------ --------------------
0.376                        262                  62500                99.456387447492                250000
2.1551724137931              232                  232                  43.2438349394613               62500
0                            1                    1                    37.2374598468001               232

Al contrario, a volte potresti voler forzare SQL Server a sottovalutare il conteggio delle righe quando sai che i dati sono già preordinati e desideri evitare un ordinamento non necessario. Un esempio notevole è quando si inserisce un numero elevato di righe in una tabella con una newsequentialidchiave di indice cluster. Nelle versioni di SQL Server precedenti a Denali, SQL Server aggiunge un'operazione di ordinamento non necessaria e potenzialmente costosa . Questo può essere evitato da

DECLARE @var INT =2147483647

INSERT INTO Foo
SELECT TOP (@var) *
FROM Bar

SQL Server stimerà quindi che verranno inserite 100 righe indipendentemente dalla dimensione Barinferiore alla soglia alla quale viene aggiunto un ordinamento al piano. Tuttavia, come sottolineato nei commenti seguenti, ciò significa che purtroppo l'inserimento non sarà in grado di sfruttare la registrazione minima.



12

Se l'ottimizzatore decide che sarebbe più efficiente ordinare i dati prima dell'inserimento, lo farà da qualche parte a monte dell'operatore di inserimento. Se si introduce un ordinamento come parte della query, l'ottimizzatore dovrebbe rendersi conto che i dati sono già ordinati e ometterlo di nuovo. Si noti che il piano di esecuzione scelto può variare da corsa a corsa a seconda del numero di righe inserite dalla tabella di gestione temporanea.

Se è possibile acquisire piani di esecuzione del processo con e senza l'ordinamento esplicito, allegarli alla domanda per un commento.

Modifica: 2011-10-28 17:00

La risposta di @ Gonsalu sembra mostrare che si verifica sempre un'operazione di ordinamento, non è così. Script demo richiesti!

Mentre gli script stavano diventando abbastanza grandi, li ho spostati in Gist . Per facilità di sperimentazione, gli script usano la modalità SQLCMD. I test vengono eseguiti su 2K5SP3, dual core, 8 GB.

I test di inserimento riguardano tre scenari:

  1. Indice cluster di dati di staging nello stesso ordine del target.
  2. Indice cluster di dati di gestione temporanea in ordine inverso.
  3. Dati di gestione temporanea raggruppati da col2 che contiene un INT casuale.

Prima esecuzione, inserendo 25 righe.

1a corsa, 25 file

Tutti e tre i piani di esecuzione sono uguali, non si verifica alcun ordinamento in alcun punto del piano e la scansione dell'indice cluster è "ordinata = falsa".

Seconda corsa, inserimento di 26 righe.

2a corsa, 26 file

Questa volta i piani differiscono.

  • Il primo mostra la scansione dell'indice cluster come ordinato = falso. Non si è verificato alcun ordinamento poiché i dati di origine sono ordinati in modo appropriato.
  • Nel secondo l'indice cluster viene scansionato come ordinato = vero, indietro. Quindi non abbiamo un'operazione di ordinamento, ma la necessità di ordinare i dati viene riconosciuta dall'ottimizzatore e scansiona in ordine inverso.
  • Il terzo mostra un operatore di ordinamento.

Quindi, c'è un punto di non ritorno in cui l'ottimizzatore ritiene necessario un ordinamento. Come mostra @MartinSmith, questo sembra essere basato sulle righe stimate da inserire. Sul mio banco di prova 25 non richiede un ordinamento, 26 lo fa (2K5SP3, dual core, 8GB)

Lo script SQLCMD include variabili che consentono di modificare la dimensione delle righe nella tabella (alterando la densità della pagina) e il numero di righe in dbo.MyTable prima degli inserimenti aggiuntivi. Dal mio test, nessuno dei due ha alcun effetto sul punto di non ritorno.

Se qualche lettore è così propenso, esegui gli script e aggiungi il tuo punto di non ritorno come commento. Interessato a sapere se varia tra i banchi prova e / o le versioni.

Modifica: 28-10-2010 20:15

Test ripetuti sullo stesso rig ma con 2K8R2. Questa volta il punto di ribaltamento è di 251 righe. Ancora una volta, variare la densità della pagina e il numero di righe esistenti non ha alcun effetto.


8

La ORDER BYclausola SELECTnell'istruzione è ridondante.

È ridondante perché le righe che verranno inserite, se devono essere ordinate , vengono comunque ordinate.

Creiamo un caso di prova.

CREATE TABLE #Test (
    id INTEGER NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX CL_Test_ID ON #Test (id);

CREATE TABLE #Sequence (
    number INTEGER NOT NULL
);

INSERT INTO #Sequence
SELECT number FROM master..spt_values WHERE name IS NULL;

Abilitiamo la visualizzazione del testo dei piani di query effettivi, in modo da poter vedere quali attività vengono eseguite dal processore di query.

SET STATISTICS PROFILE ON;
GO

Ora, INSERT2K righe nella tabella senza una ORDER BYclausola.

INSERT INTO #Test
SELECT number
  FROM #Sequence

Il piano di esecuzione effettivo per questa query è il seguente.

INSERT INTO #Test  SELECT number    FROM #Sequence
  |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
       |--Top(ROWCOUNT est 0)
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))

Come puoi vedere, c'è un operatore di ordinamento prima che si verifichi l'INSERIMENTO effettivo.

Ora, cancelliamo la tabella e INSERT2k righe nella tabella con la ORDER BYclausola.

TRUNCATE TABLE #Test;
GO

INSERT INTO #Test
SELECT number
  FROM #Sequence
 ORDER BY number

Il piano di esecuzione effettivo per questa query è il seguente.

INSERT INTO #Test  SELECT number    FROM #Sequence   ORDER BY number
  |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
       |--Top(ROWCOUNT est 0)
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))

Si noti che è lo stesso piano di esecuzione utilizzato per l' INSERTistruzione senza la ORDER BYclausola.

Ora, l' Sortoperazione non è sempre richiesta, come Mark Smith ha mostrato in un'altra risposta (se il numero di righe da inserire è basso), ma la ORDER BYclausola è ancora ridondante in quel caso, perché anche con un esplicito ORDER BY, non Sortviene generata alcuna operazione dal processore di query.

È possibile ottimizzare INSERTun'istruzione in una tabella con un indice cluster, utilizzando un registro minimo INSERT, ma non rientra nell'ambito di questa domanda.

Aggiornato 2011-11-02: Come ha dimostrato Mark Smith , INSERTs in una tabella con un indice cluster potrebbe non richiedere sempre di essere ordinato ORDER BY, ma in questo caso la clausola è anche ridondante.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.