SQL, Ausiliari tabella di numeri
-
08-06-2019 - |
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?
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),']'));
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.