Domanda

Per alcuni tipi di query sql, una tabella ausiliaria di numeri può essere molto utile.Potrebbe essere creata una tabella con tante righe quanti avete bisogno di per un particolare compito o una funzione definita dall'utente che restituisce il numero di righe necessarie in ogni query.

Qual è il modo migliore per creare un tale funzione?

È stato utile?

Soluzione

Eh...mi dispiace sono così in ritardo rispondere ad un post vecchio.E, sì, ho dovuto rispondere, perché la risposta più popolare (al momento, il CTE Ricorsiva risposta con il link a 14 diversi metodi) su questo thread, ummm...prestazioni contestato al meglio.

Primo, l'articolo con le 14 diverse soluzioni è bene per vedere i diversi metodi per la creazione di un numero/Tally tabella al volo, ma come sottolineato nell'articolo e nel citato thread, c'è un molto citazione importante...

"suggerimenti per quanto riguarda l'efficienza e la le prestazioni sono spesso soggettive.Indipendentemente dalla modalità di una query utilizzato, l'implementazione fisica determina l'efficienza di una query.Quindi, piuttosto che fare affidamento su polarizzato guida, è imperativo testare le query e determinare quale funziona meglio."

Ironia della sorte, lo stesso articolo contiene molte dichiarazioni soggettive e "di parte linee guida" come "un CTE ricorsiva in grado di generare un numero di quotazione piuttosto efficiente" e "Questo è un metodo efficiente di utilizzo DURANTE il ciclo da un newsgroup da Itzik Ben-Gen" (che sono sicuro che ha postato solo per fini di confronto).Suvvia gente...Basta ricordare Itzik buon nome può portare a qualche povero slob in realtà utilizzando l'orribile metodo.L'autore dovrebbero mettere in pratica quello che predica e deve fare un po ' di test delle prestazioni prima di procedere a tale ridicolmente dichiarazioni inesatte soprattutto di fronte a qualsiasi scalablility.

Con il pensiero di fare qualche prova prima di effettuare qualsiasi pretese soggettive su ciò che qualsiasi codice o che qualcuno "mi piace", ecco un po ' di codice è possibile fare i tuoi test con.L'installazione di profiler per lo SPID si sta eseguendo il test e check it out per te...basta fare un "Cerca n'Replace" del numero 1000000 per il tuo "preferito" numero e vedere...

--===== Test for 1000000 rows ==================================
GO
--===== Traditional RECURSIVE CTE method
   WITH Tally (N) AS 
        ( 
         SELECT 1 UNION ALL 
         SELECT 1 + N FROM Tally WHERE N < 1000000 
        ) 
 SELECT N 
   INTO #Tally1 
   FROM Tally 
 OPTION (MAXRECURSION 0);
GO
--===== Traditional WHILE LOOP method
 CREATE TABLE #Tally2 (N INT);
    SET NOCOUNT ON;
DECLARE @Index INT;
    SET @Index = 1;
  WHILE @Index <= 1000000 
  BEGIN 
         INSERT #Tally2 (N) 
         VALUES (@Index);
            SET @Index = @Index + 1;
    END;
GO
--===== Traditional CROSS JOIN table method
 SELECT TOP (1000000)
        ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS N
   INTO #Tally3
   FROM Master.sys.All_Columns ac1
  CROSS JOIN Master.sys.ALL_Columns ac2;
GO
--===== Itzik's CROSS JOINED CTE method
   WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
        E02(N) AS (SELECT 1 FROM E00 a, E00 b),
        E04(N) AS (SELECT 1 FROM E02 a, E02 b),
        E08(N) AS (SELECT 1 FROM E04 a, E04 b),
        E16(N) AS (SELECT 1 FROM E08 a, E08 b),
        E32(N) AS (SELECT 1 FROM E16 a, E16 b),
   cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32)
 SELECT N
   INTO #Tally4
   FROM cteTally
  WHERE N <= 1000000;
GO
--===== Housekeeping
   DROP TABLE #Tally1, #Tally2, #Tally3, #Tally4;
GO

Mentre che ci siamo, ecco i numeri mi da SQL Profiler per i valori di 100, 1000, 10000, 100000, e 1000000...

SPID TextData                                 Dur(ms) CPU   Reads   Writes
---- ---------------------------------------- ------- ----- ------- ------
  51 --===== Test for 100 rows ==============       8     0       0      0
  51 --===== Traditional RECURSIVE CTE method      16     0     868      0
  51 --===== Traditional WHILE LOOP method CR      73    16     175      2
  51 --===== Traditional CROSS JOIN table met      11     0      80      0
  51 --===== Itzik's CROSS JOINED CTE method        6     0      63      0
  51 --===== Housekeeping   DROP TABLE #Tally      35    31     401      0

  51 --===== Test for 1000 rows =============       0     0       0      0
  51 --===== Traditional RECURSIVE CTE method      47    47    8074      0
  51 --===== Traditional WHILE LOOP method CR      80    78    1085      0
  51 --===== Traditional CROSS JOIN table met       5     0      98      0
  51 --===== Itzik's CROSS JOINED CTE method        2     0      83      0
  51 --===== Housekeeping   DROP TABLE #Tally       6    15     426      0

  51 --===== Test for 10000 rows ============       0     0       0      0
  51 --===== Traditional RECURSIVE CTE method     434   344   80230     10
  51 --===== Traditional WHILE LOOP method CR     671   563   10240      9
  51 --===== Traditional CROSS JOIN table met      25    31     302     15
  51 --===== Itzik's CROSS JOINED CTE method       24     0     192     15
  51 --===== Housekeeping   DROP TABLE #Tally       7    15     531      0

  51 --===== Test for 100000 rows ===========       0     0       0      0
  51 --===== Traditional RECURSIVE CTE method    4143  3813  800260    154
  51 --===== Traditional WHILE LOOP method CR    5820  5547  101380    161
  51 --===== Traditional CROSS JOIN table met     160   140     479    211
  51 --===== Itzik's CROSS JOINED CTE method      153   141     276    204
  51 --===== Housekeeping   DROP TABLE #Tally      10    15     761      0

  51 --===== Test for 1000000 rows ==========       0     0       0      0
  51 --===== Traditional RECURSIVE CTE method   41349 37437 8001048   1601
  51 --===== Traditional WHILE LOOP method CR   59138 56141 1012785   1682
  51 --===== Traditional CROSS JOIN table met    1224  1219    2429   2101
  51 --===== Itzik's CROSS JOINED CTE method     1448  1328    1217   2095
  51 --===== Housekeeping   DROP TABLE #Tally       8     0     415      0

Come si può vedere, CTE Ricorsiva del metodo è la seconda peggiore dei casi, solo per il Ciclo While per la Durata che per la CPU e dispone di 8 volte la pressione di memoria in forma di letture logiche che il Ciclo While.È RBAR su steroidi e dovrebbe essere evitato a tutti i costi, per ogni singola riga calcoli come un Ciclo While dovrebbe essere evitato. Ci sono luoghi dove la ricorsione è molto prezioso, ma questo NON È uno di loro.

Come una barra laterale, il SignorDenny è assolutamente a posto...un dimensionati correttamente, permanente o Numeri di Tally tabella è la strada da percorrere per la maggior parte delle cose.Cosa dimensionato correttamente in media?Beh, la maggior parte delle persone utilizza un Riscontro tabella di generare date o per fare divide su VARCHAR(8000).Se si crea un'area di 11.000 riga Tally tabella con il corretto indice cluster su "N", si avrà abbastanza righe per creare più di 30 anni vale la pena di date (io lavoro con mutui per un po ' così a 30 anni è un numero chiave per me), e certamente più che sufficiente per gestire un VARCHAR(8000) split.Perché è "right sizing" è così importante?Se il Conteggio viene utilizzata la tabella di molto, si adatta facilmente in una cache, il che lo rende incredibilmente veloce e senza troppa pressione sulla memoria a tutti.

Ultimo ma non meno importante, tutti sanno che se si crea una permanente Tally tabella, poco importa quale metodo si utilizza per costruire, perché 1) è solo andare a essere fatta una volta e 2) se si tratta di qualcosa come 11.000 riga della tabella, tutti i metodi sono andando a correre "abbastanza buono". E allora perché tutti i indigination da parte mia su quale metodo usare???

La risposta è che qualche povero ragazzo/ragazza che non conoscono meglio e ha solo bisogno di ottenere il suo lavoro fatto potrebbe vedere qualcosa di simile CTE Ricorsiva del metodo e decide di usarlo per qualcosa di molto più grande e molto più frequentemente utilizzati rispetto alla costruzione di un permanente Tally tavolo e sto cercando di proteggere quelle persone, i server loro codice è in esecuzione, e l'impresa che possiede i dati su tali server.Sì...è un grande affare.Dovrebbe essere per tutti gli altri, pure.Insegnare il modo giusto di fare le cose invece di "abbastanza buono".Fare qualche test prima di pubblicare o utilizzare qualcosa da un post o di un libro...la vita si salva può, infatti, essere il vostro, soprattutto se si pensa che un CTE ricorsiva è il modo di andare per qualcosa di simile a questo.;-)

Grazie per l'ascolto...

Altri suggerimenti

La maggior funzione ottimale sarebbe quella di utilizzare una tabella invece di una funzione.Utilizzando una funzione di cause extra carico della CPU per creare i valori per la restituzione di dati, soprattutto se i valori restituiti coprire una gamma molto ampia.

Questo articolo dà 14 diverse possibili soluzioni con la discussione di ciascuno di essi.Il punto importante è che:

suggerimenti per quanto riguarda l'efficienza e la le prestazioni sono spesso soggettive.Indipendentemente dalla modalità di una query utilizzato, l'implementazione fisica determina l'efficienza di una query.Quindi, piuttosto che fare affidamento su polarizzato guida, è imperativo testare le query e determinare quale funziona meglio.

Personalmente mi è piaciuto:

WITH Nbrs ( n ) AS (
    SELECT 1 UNION ALL
    SELECT 1 + n FROM Nbrs WHERE n < 500 )
SELECT n FROM Nbrs
OPTION ( MAXRECURSION 500 )

Questo punto di vista è super veloce e contiene tutti positivi int valori.

CREATE VIEW dbo.Numbers
WITH SCHEMABINDING
AS
    WITH Int1(z) AS (SELECT 0 UNION ALL SELECT 0)
    , Int2(z) AS (SELECT 0 FROM Int1 a CROSS JOIN Int1 b)
    , Int4(z) AS (SELECT 0 FROM Int2 a CROSS JOIN Int2 b)
    , Int8(z) AS (SELECT 0 FROM Int4 a CROSS JOIN Int4 b)
    , Int16(z) AS (SELECT 0 FROM Int8 a CROSS JOIN Int8 b)
    , Int32(z) AS (SELECT TOP 2147483647 0 FROM Int16 a CROSS JOIN Int16 b)
    SELECT ROW_NUMBER() OVER (ORDER BY z) AS n
    FROM Int32
GO

Utilizzando SQL Server 2016+ per generare i numeri di tabella che si potrebbe utilizzare OPENJSON :

-- range from 0 to @max - 1
DECLARE @max INT = 40000;

SELECT rn = CAST([key] AS INT) 
FROM OPENJSON(CONCAT('[1', REPLICATE(CAST(',1' AS VARCHAR(MAX)),@max-1),']'));

LiveDemo


Idea presa da Come possiamo utilizzare OPENJSON per generare una serie di numeri?

edit:vedere Conrad commento qui sotto.

Jeff Moden la risposta è grande ...ma trovo a Postgres che il Itzik metodo ha esito negativo a meno che non si rimuove il E32 riga.

Leggermente più veloce su postgres (40ms vs 100ms) è un altro metodo che ho trovato su qui adattato per postgres:

WITH 
    E00 (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 ),
    E01 (N) AS (SELECT a.N FROM E00 a CROSS JOIN E00 b),
    E02 (N) AS (SELECT a.N FROM E01 a CROSS JOIN E01 b ),
    E03 (N) AS (SELECT a.N FROM E02 a CROSS JOIN E02 b 
        LIMIT 11000  -- end record  11,000 good for 30 yrs dates
    ), -- max is 100,000,000, starts slowing e.g. 1 million 1.5 secs, 2 mil 2.5 secs, 3 mill 4 secs
    Tally (N) as (SELECT row_number() OVER (ORDER BY a.N) FROM E03 a)

SELECT N
FROM Tally

Mentre sono in movimento da SQL Server Postgres mondo, hanno perso un modo migliore per fare tally tabelle su quella piattaforma ...INTEGER()?SEQUENZA()?

Ancora più tardi, vorrei contribuire leggermente diversa 'tradizionale' CTE (non toccare le tabelle di base per ottenere il volume di righe):

--===== Hans CROSS JOINED CTE method
WITH Numbers_CTE (Digit)
AS
(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9)
SELECT HundredThousand.Digit * 100000 + TenThousand.Digit * 10000 + Thousand.Digit * 1000 + Hundred.Digit * 100 + Ten.Digit * 10 + One.Digit AS Number
INTO #Tally5
FROM Numbers_CTE AS One CROSS JOIN Numbers_CTE AS Ten CROSS JOIN Numbers_CTE AS Hundred CROSS JOIN Numbers_CTE AS Thousand CROSS JOIN Numbers_CTE AS TenThousand CROSS JOIN Numbers_CTE AS HundredThousand

Questo CTE esegue più Legge poi Itzik del CTE, ma meno Tradizionale CTE.Tuttavia, esso esegue meno SCRIVE poi le altre query. Come sapete, Scrive sono sempre molto più costoso di Legge.

La durata dipende dal numero di core (MAXDOP) ma, sul mio 8core, esegue costantemente più veloce (meno durata in ms) poi le altre query.

Io sto utilizzando:

Microsoft SQL Server 2012 - 11.0.5058.0 (X64) 
May 14 2014 18:34:29 
Copyright (c) Microsoft Corporation
Enterprise Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: )

in Windows Server 2012 R2, 32 GB, Xeon X3450 @2.67 Ghz, 4 core HT abilitato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top