Heh... omlouvám se, že reaguji tak pozdě na starý příspěvek. A ano, musel jsem odpovědět, protože nejoblíbenější odpověď (v té době odpověď Rekurzivní CTE s odkazem na 14 různých metod) v tomto vláknu je, ehm... výkon je přinejlepším zpochybněn.
Za prvé, článek se 14 různými řešeními je v pořádku, protože vidíte různé metody vytváření tabulky Numbers/Tally za chodu, ale jak je uvedeno v článku a v citovaném vláknu, existuje velmi důležitý citát...
"Návrhy týkající se efektivity a výkonu jsou často subjektivní. Bez ohledu na to, jak je dotaz používán, určuje efektivitu dotazu fyzická implementace. Proto raději než se spoléhat na neobjektivní pokyny, je nutné dotaz otestovat a určit, který z nich funguje lépe."
Je ironií, že samotný článek obsahuje mnoho subjektivních prohlášení a „objektivních pokynů“, jako je „rekurzivní CTE může generovat číselný výpis velmi efektivně " a „Toto je účinná metoda použití smyčky WHILE z diskusní skupiny od Itzika Ben-Gena" (což jsem si jistý, že zveřejnil jen pro účely srovnání). No tak lidi... Pouhá zmínka o Itzikově dobrém jménu může vést některého ubohého flákače k tomu, aby skutečně použil tu strašlivou metodu. Autor by si měl procvičit to, co káže, a měl by udělat malý test výkonu, než učiní taková směšně nesprávná prohlášení, zejména tváří v tvář jakékoli škálovatelnosti.
S myšlenkou na to, že skutečně provedete nějaké testování před tím, než začnete subjektivně tvrdit, co který kód dělá nebo co se někomu „líbí“, zde je nějaký kód, se kterým můžete provádět vlastní testování. Nastavte profilovač pro SPID, ze kterého spouštíte test, a sami si to ověřte... jednoduše zadejte "Search'n'Replace" číslo 1000000 pro své "oblíbené" číslo a uvidíte...
--===== 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
Když jsme u toho, zde jsou čísla, která získám z SQL Profiler pro hodnoty 100, 1000, 10000, 100000 a 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
Jak můžete vidět, metoda rekurzivního CTE je druhá nejhorší za smyčkou While z hlediska trvání a CPU a má 8krát větší tlak na paměť ve formě logických čtení než smyčka While . Je to RBAR na steroidech a je třeba se mu za každou cenu vyhnout pro výpočty s jedním řádkem, stejně jako je třeba se vyhnout smyčkám While. Existují místa, kde je rekurze docela cenná, ale tohle NENÍ jedno z nich .
Jako postranní lišta je pan Denny naprosto na místě... pro většinu věcí je správná velikost stálé tabulky čísel nebo sčítání. Co znamená správná velikost? Většina lidí používá tabulku sčítání ke generování dat nebo k rozdělení na VARCHAR(8000). Pokud vytvoříte tabulku Tally s 11 000 řádky se správným seskupeným indexem na „N“, budete mít dostatek řádků k vytvoření dat za více než 30 let (s hypotékami pracuji docela dost, takže 30 let je pro mě klíčové číslo ) a určitě dost na to, aby zvládl rozdělení VARCHAR(8000). Proč je „správná velikost“ tak důležitá? Pokud je tabulka Tally hodně používána, snadno se vejde do mezipaměti, díky čemuž je neuvěřitelně rychlá bez velkého tlaku na paměť.
V neposlední řadě každý ví, že pokud vytvoříte trvalou tabulku Tally, nezáleží na tom, jakou metodou ji sestavíte, protože 1) bude vytvořena pouze jednou a 2) pokud je to něco jako 11 000 řádek tabulky, všechny metody poběží "dost dobře". Tak proč tolik rozhořčení z mé strany ohledně toho, kterou metodu použít???
Odpověď zní, že nějaký chudák/holka, která se v ničem lépe nevyzná a potřebuje jen dokončit svou práci, může vidět něco jako metodu rekurzivního CTE a rozhodnout se ji použít pro něco mnohem většího a mnohem častěji používaného, než je stavba. trvalou tabulku Tally a já se snažím chránit tyto lidi, servery, na kterých běží jejich kód, a společnost, která vlastní data na těchto serverech . Jo... je to tak velký problém. Mělo by to být i pro všechny ostatní. Učte správný způsob, jak dělat věci místo „dost dobré“. Před odesláním nebo použitím něčeho z příspěvku nebo knihy proveďte nějaké testování... život, který zachráníte, může být ve skutečnosti váš vlastní, zvláště pokud si myslíte, že rekurzivní CTE je způsob, jak se k něčemu takovému vydat.;-)
Děkujeme za poslech...