Heh... scusa se rispondo così in ritardo a un vecchio post. E, sì, ho dovuto rispondere perché la risposta più popolare (all'epoca, la risposta CTE ricorsiva con il collegamento a 14 metodi diversi) su questo thread è, ummm... le prestazioni sono state al massimo sfidate.
Innanzitutto l'articolo con le 14 diverse soluzioni va bene per vedere al volo i diversi metodi per creare una tabella Numeri/Tally ma come sottolineato nell'articolo e nel thread citato, c'è un molto citazione importante...
"i suggerimenti relativi all'efficienza e alle prestazioni sono spesso soggettivi. Indipendentemente da come viene utilizzata una query, l'implementazione fisica determina l'efficienza di una query. Pertanto, anziché basarsi su linee guida distorte, è imperativo testare la query e determinare quale funziona meglio."
Ironia della sorte, l'articolo stesso contiene molte affermazioni soggettive e "linee guida distorte" come "un CTE ricorsivo può generare un elenco di numeri abbastanza efficientemente " e "Questo è un metodo efficiente di usare il ciclo WHILE da un post in un newsgroup di Itzik Ben-Gen" (che sono sicuro che ha pubblicato solo a scopo di confronto). Andiamo gente... Il solo fatto di menzionare il buon nome di Itzik può portare qualche povero sciatto a usare effettivamente quel metodo orribile. L'autore dovrebbe esercitarsi su ciò che predica e dovrebbe fare un piccolo test delle prestazioni prima di fare affermazioni così ridicolmente errate, specialmente di fronte a qualsiasi scalabilità.
Con l'idea di eseguire effettivamente dei test prima di fare affermazioni soggettive su ciò che fa qualsiasi codice o su ciò che "piace a qualcuno", ecco del codice con cui puoi eseguire i tuoi test. Configura il profiler per lo SPID da cui stai eseguendo il test e verifica tu stesso... fai semplicemente un "Search'n'Replace" del numero 1000000 per il tuo numero "preferito" e vedi...
--===== 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
Già che ci siamo, ecco i numeri che ottengo 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 puoi vedere, il metodo CTE ricorsivo è il secondo peggiore solo al ciclo While per durata e CPU e ha una pressione di memoria 8 volte superiore sotto forma di letture logiche rispetto al ciclo While . È RBAR sotto steroidi e dovrebbe essere evitato, a tutti i costi, per qualsiasi calcolo di riga singola proprio come dovrebbe essere evitato un ciclo While. Ci sono luoghi in cui la ricorsione è piuttosto preziosa, ma questo NON È uno di questi .
Come barra laterale, il signor Denny è assolutamente perfetto... un tavolo Numbers o Tally permanente di dimensioni corrette è la strada da percorrere per la maggior parte delle cose. Cosa significa taglia corretta? Bene, la maggior parte delle persone usa una tabella Tally per generare date o per fare divisioni su VARCHAR(8000). Se crei una tabella Tally di 11.000 righe con l'indice raggruppato corretto su "N", avrai abbastanza righe per creare più di 30 anni di date (lavoro un bel po' con i mutui, quindi 30 anni sono un numero chiave per me ) e certamente sufficiente per gestire una divisione VARCHAR(8000). Perché la "taglia giusta" è così importante? Se la tabella Tally viene utilizzata molto, si inserisce facilmente nella cache, il che la rende incredibilmente veloce senza troppa pressione sulla memoria.
Ultimo ma non meno importante, tutti sanno che se crei una tabella Tally permanente, non importa quale metodo usi per costruirla perché 1) verrà creata solo una volta e 2) se è qualcosa come una riga di 11.000 table, tutti i metodi funzioneranno "abbastanza bene". Allora perché tutta l'indiginazione da parte mia su quale metodo usare???
La risposta è che qualche povero ragazzo/ragazza che non sa niente di meglio e ha solo bisogno di portare a termine il proprio lavoro potrebbe vedere qualcosa come il metodo CTE ricorsivo e decidere di usarlo per qualcosa di molto più grande e molto più frequentemente utilizzato rispetto alla costruzione una tabella Tally permanente e sto cercando di proteggere quelle persone, i server su cui gira il loro codice e la società che possiede i dati su quei server . Sì... è un grosso problema. Dovrebbe esserlo anche per tutti gli altri. Insegna il modo giusto di fare le cose invece di "abbastanza buono". Fai alcuni test prima di pubblicare o utilizzare qualcosa da un post o un libro... la vita che salvi potrebbe, infatti, essere la tua, soprattutto se pensi che un CTE ricorsivo sia la strada da percorrere per qualcosa del genere.;-)
Grazie per l'ascolto...