Nel mio post precedente, ho parlato dei modi per generare una sequenza di numeri contigui da 1 a 1.000. Ora vorrei parlare dei prossimi livelli di scala:gruppi elettrogeni di 50.000 e 1.000.000 di numeri.
Generazione di un set di 50.000 numeri
Quando ho iniziato questa serie, ero sinceramente curioso di sapere come i diversi approcci si sarebbero adattati a insiemi di numeri più grandi. Nella fascia bassa sono rimasto un po' costernato nello scoprire che il mio approccio preferito:usare sys.all_objects
– non era il metodo più efficiente. Ma in che modo queste diverse tecniche scalerebbero fino a 50.000 righe?
Tabella dei numeri
Poiché abbiamo già creato una tabella di Numbers con 1.000.000 di righe, questa query rimane praticamente identica:
SELECT TOP (50000) n FROM dbo.Numbers ORDER BY n;
Piano:
valori_spt
Poiché ci sono solo ~2.500 righe in spt_values
, dobbiamo essere un po' più creativi se vogliamo usarlo come fonte del nostro generatore di set. Un modo per simulare una tabella più grande è CROSS JOIN
esso contro se stesso. Se lo facessimo grezzo, ci ritroveremmo con circa 2.500 righe al quadrato (oltre 6 milioni). Avendo bisogno di sole 50.000 righe, abbiamo bisogno di circa 224 righe al quadrato. Quindi possiamo farlo:
;WITH x AS ( SELECT TOP (224) number FROM [master]..spt_values ) SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.number) FROM x CROSS JOIN x AS y ORDER BY n;
Nota che questo è equivalente, ma più conciso, di questa variazione:
SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.number) FROM (SELECT TOP (224) number FROM [master]..spt_values) AS x CROSS JOIN (SELECT TOP (224) number FROM [master]..spt_values) AS y ORDER BY n;
In entrambi i casi, il piano si presenta così:
sys.all_objects
Come spt_values
, sys.all_objects
non soddisfa del tutto il nostro requisito di 50.000 righe da solo, quindi dovremo eseguire un CROSS JOIN
simile .
;;WITH x AS ( SELECT TOP (224) [object_id] FROM sys.all_objects ) SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.[object_id]) FROM x CROSS JOIN x AS y ORDER BY n;
Piano:
CTE impilati
Abbiamo solo bisogno di apportare una piccola modifica ai nostri CTE impilati per ottenere esattamente 50.000 righe:
;WITH e1(n) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ), -- 10 e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10 e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100 e4(n) AS (SELECT 1 FROM e3 CROSS JOIN (SELECT TOP 5 n FROM e1) AS b) -- 5*10000 SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;
Piano:
CTE ricorsivi
Una modifica ancora meno sostanziale è necessaria per ottenere 50.000 righe dal nostro CTE ricorsivo:cambia il WHERE
clausola a 50.000 e modificare il MAXRECURSION
opzione a zero.
;WITH n(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n < 50000 ) SELECT n FROM n ORDER BY n OPTION (MAXRECURSION 0);
Piano:
In questo caso c'è un'icona di avviso sull'ordinamento, a quanto pare, sul mio sistema, l'ordinamento doveva essere versato su tempdb. Potresti non vedere una fuoriuscita sul tuo sistema, ma questo dovrebbe essere un avviso sulle risorse richieste per questa tecnica.
Prestazioni
Come per l'ultima serie di test, confronteremo ciascuna tecnica, inclusa la tabella Numbers con una cache calda e fredda, sia compressa che non compressa:
Runtime, in millisecondi, per generare 50.000 numeri contigui
Per ottenere una visuale migliore, rimuoviamo il CTE ricorsivo, che era un cane totale in questo test e che distorce i risultati:
Runtime, in millisecondi, per generare 50.000 numeri contigui (esclusi ricorsivi CTE)
A 1.000 righe, la differenza tra compresso e non compresso era marginale, poiché la query doveva leggere rispettivamente solo 8 e 9 pagine. A 50.000 righe, il divario si allarga un po':74 pagine contro 113. Tuttavia, il costo complessivo della decompressione dei dati sembra superare i risparmi in I/O. Quindi, a 50.000 righe, una tabella di numeri non compressa sembra essere il metodo più efficiente per derivare un insieme contiguo, anche se, è vero, il vantaggio è marginale.
Generazione di un set di 1.000.000 di numeri
Anche se non riesco a immaginare molti casi d'uso in cui avresti bisogno di un insieme contiguo di numeri così grande, volevo includerlo per completezza e perché ho fatto alcune osservazioni interessanti su questa scala.
Tabella dei numeri
Nessuna sorpresa qui, la nostra domanda ora è:
SELECT TOP 1000000 n FROM dbo.Numbers ORDER BY n;
Il TOP
non è strettamente necessario, ma è solo perché sappiamo che la nostra tabella Numbers e l'output desiderato hanno lo stesso numero di righe. Il piano è ancora abbastanza simile ai test precedenti:
valori_spt
Per ottenere un CROSS JOIN
che produce 1.000.000 di righe, dobbiamo prendere 1.000 righe al quadrato:
;WITH x AS ( SELECT TOP (1000) number FROM [master]..spt_values ) SELECT n = ROW_NUMBER() OVER (ORDER BY x.number) FROM x CROSS JOIN x AS y ORDER BY n;
Piano:
sys.all_objects
Ancora una volta, abbiamo bisogno del prodotto incrociato di 1.000 righe:
;WITH x AS ( SELECT TOP (1000) [object_id] FROM sys.all_objects ) SELECT n = ROW_NUMBER() OVER (ORDER BY x.[object_id]) FROM x CROSS JOIN x AS y ORDER BY n;
Piano:
CTE impilati
Per il CTE in pila, abbiamo solo bisogno di una combinazione leggermente diversa di CROSS JOIN
s per arrivare a 1.000.000 di righe:
;WITH e1(n) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ), -- 10 e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10 e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2 AS b), -- 10*100 e4(n) AS (SELECT 1 FROM e3 CROSS JOIN e3 AS b) -- 1000*1000 SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;
Piano:
A queste dimensioni di riga, puoi vedere che la soluzione CTE impilata diventa parallela. Quindi ho eseguito anche una versione con MAXDOP 1
per ottenere una forma del piano simile a prima e per vedere se il parallelismo aiuta davvero:
CTE ricorsivo
Il CTE ricorsivo ha di nuovo solo una piccola modifica; solo il WHERE
la clausola deve cambiare:
;WITH n(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n < 1000000 ) SELECT n FROM n ORDER BY n OPTION (MAXRECURSION 0);
Piano:
Prestazioni
Ancora una volta vediamo che le prestazioni del CTE ricorsivo sono pessime:
Runtime, in millisecondi, per generare 1.000.000 di numeri contigui
Rimuovendo questo valore anomalo dal grafico, otteniamo un quadro migliore delle prestazioni:
Runtime, in millisecondi, per generare 1.000.000 di numeri contigui (escluso ricorsivo CTE)
Anche se vediamo ancora una volta la tabella dei numeri non compressa (almeno con una cache calda) come vincitrice, la differenza anche su questa scala non è poi così notevole.
Continua...
Ora che abbiamo esplorato a fondo una manciata di approcci per generare una sequenza di numeri, passeremo alle date. Nel post finale di questa serie, analizzeremo la costruzione di un intervallo di date come set, incluso l'uso di una tabella del calendario, e alcuni casi d'uso in cui può essere utile.
[ Parte 1 | Parte 2 | Parte 3]
Appendice:Conteggio righe
Potresti non provare a generare un numero esatto di righe; potresti invece volere solo un modo semplice per generare molte righe. Quello che segue è un elenco di combinazioni di viste del catalogo che ti daranno vari conteggi di righe se semplicemente SELECT
senza un WHERE
clausola. Tieni presente che questi numeri dipenderanno dal fatto che tu sia su un RTM o un service pack (poiché alcuni oggetti di sistema vengono aggiunti o modificati) e anche se hai un database vuoto.
Fonte | Conteggio delle righe | ||
---|---|---|---|
SQL Server 2008 R2 | SQL Server 2012 | SQL Server 2014 | |
master..spt_values | 2.508 | 2.515 | 2.519 |
master..spt_values CROSS JOIN master..spt_values | 6.290.064 | 6.325.225 | 6.345.361 |
sys.all_objects | 1.990 | 2.089 | 2.165 |
sys.all_columns | 5.157 | 7.276 | 8.560 |
sys.all_objects CROSS JOIN sys.all_objects | 3.960.100 | 4.363.921 | 4.687.225 |
sys.all_objects CROSS JOIN sys.all_columns | 10.262.430 | 15.199.564 | 18.532.400 |
sys.all_columns CROSS JOIN sys.all_columns | 26.594.649 | 52.940.176 | 73.273.600 |
Tabella 1:numero di righe per varie query di visualizzazione del catalogo