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

Genera un set o una sequenza senza loop – parte 2

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