In meinem vorherigen Beitrag habe ich darüber gesprochen, wie man eine Folge zusammenhängender Zahlen von 1 bis 1.000 generieren kann. Jetzt möchte ich über die nächsten Skalenebenen sprechen:Generieren von Sätzen mit 50.000 und 1.000.000 Zahlen.
Generieren eines Satzes von 50.000 Nummern
Als ich mit dieser Serie begann, war ich wirklich neugierig, wie sich die verschiedenen Ansätze auf größere Zahlenmengen skalieren würden. Am unteren Ende war ich ein wenig bestürzt, als ich herausfand, dass mein Lieblingsansatz die Verwendung von sys.all_objects
ist – war nicht die effizienteste Methode. Aber wie lassen sich diese unterschiedlichen Techniken auf 50.000 Zeilen skalieren?
Zahlentabelle
Da wir bereits eine Numbers-Tabelle mit 1.000.000 Zeilen erstellt haben, bleibt diese Abfrage praktisch identisch:
SELECT TOP (50000) n FROM dbo.Numbers ORDER BY n;
Plan:
spt_values
Da es nur ~2.500 Zeilen in spt_values
gibt , müssen wir etwas kreativer sein, wenn wir es als Quelle für unseren Set-Generator verwenden wollen. Eine Möglichkeit, eine größere Tabelle zu simulieren, ist CROSS JOIN
es gegen sich. Wenn wir das roh machen würden, würden wir am Ende ~2.500 Zeilen im Quadrat (über 6 Millionen) haben. Wenn wir nur 50.000 Zeilen benötigen, brauchen wir ungefähr 224 Zeilen im Quadrat. So können wir das tun:
;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 yORDER BY n;
Beachten Sie, dass dies äquivalent, aber prägnanter als diese Variante ist:
SELECT TOP (50000) n =ROW_NUMBER() OVER (ORDER BY x.number) FROM (SELECT TOP (224) number FROM [master]..spt_values) AS xCROSS JOIN(SELECT TOP (224) number FROM [master ]..spt_values) AS yORDER BY n;
In beiden Fällen sieht der Plan so aus:
sys.all_objects
Wie spt_values
, sys.all_objects
erfüllt unsere Anforderung von 50.000 Zeilen allein nicht ganz, daher müssen wir einen ähnlichen CROSS JOIN
ausführen .
;;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;
Plan:
Gestapelte CTEs
Wir müssen nur eine kleine Anpassung an unseren gestapelten CTEs vornehmen, um genau 50.000 Zeilen zu erhalten:
;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) , -- 10e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100e4(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;
Plan:
Rekursive CTEs
Eine noch weniger wesentliche Änderung ist erforderlich, um 50.000 Zeilen aus unserem rekursiven CTE herauszuholen:Ändern Sie das WHERE
-Klausel auf 50.000 und ändern Sie MAXRECURSION
Option auf Null.
;MIT n(n) AS( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n <50000)SELECT n FROM n ORDER BY nOPTION (MAXRECURSION 0);
Plan:
In diesem Fall gibt es ein Warnsymbol auf der Sortierung – wie sich herausstellt, muss die Sortierung auf meinem System in tempdb übergehen. Möglicherweise sehen Sie auf Ihrem System keinen Überlauf, aber dies sollte eine Warnung bezüglich der für diese Technik erforderlichen Ressourcen sein.
Leistung
Wie bei den letzten Tests vergleichen wir jede Technik, einschließlich der Numbers-Tabelle, sowohl mit einem kalten als auch mit einem warmen Cache sowie mit komprimiertem und unkomprimiertem Cache:
Laufzeit in Millisekunden zum Generieren von 50.000 zusammenhängenden Nummern
Um eine bessere Darstellung zu erhalten, entfernen wir den rekursiven CTE, der in diesem Test ein totaler Hund war und die Ergebnisse verzerrt:
Laufzeit in Millisekunden zum Generieren von 50.000 zusammenhängenden Zahlen (ohne rekursive CTE)
Bei 1.000 Zeilen war der Unterschied zwischen komprimiert und unkomprimiert marginal, da die Abfrage nur 8 bzw. 9 Seiten lesen musste. Bei 50.000 Zeilen vergrößert sich die Lücke etwas:74 Seiten gegenüber 113. Die Gesamtkosten für die Dekomprimierung der Daten scheinen jedoch die Einsparungen bei der E/A zu überwiegen. Bei 50.000 Zeilen scheint eine unkomprimierte Zahlentabelle also die effizienteste Methode zu sein, um eine zusammenhängende Menge abzuleiten – allerdings ist der Vorteil zugegebenermaßen marginal.
Generieren eines Satzes von 1.000.000 Nummern
Ich kann mir zwar nicht viele Anwendungsfälle vorstellen, in denen Sie eine so große zusammenhängende Menge von Zahlen benötigen, aber ich wollte sie der Vollständigkeit halber einbeziehen und weil ich einige interessante Beobachtungen in dieser Größenordnung gemacht habe.
Zahlentabelle
Hier gibt es keine Überraschungen, unsere Abfrage lautet jetzt:
SELECT TOP 1000000 n FROM dbo.Numbers ORDER BY n;
Die TOP
ist nicht unbedingt erforderlich, aber nur, weil wir wissen, dass unsere Numbers-Tabelle und unsere gewünschte Ausgabe die gleiche Anzahl von Zeilen haben. Der Plan ist immer noch ziemlich ähnlich zu früheren Tests:
spt_values
Um einen CROSS JOIN
zu erhalten das ergibt 1.000.000 Zeilen, wir müssen 1.000 Zeilen zum Quadrat nehmen:
;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;
Plan:
sys.all_objects
Auch hier brauchen wir das Kreuzprodukt von 1.000 Zeilen:
;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;Plan:
Gestapelte CTEs
Für den gestapelten CTE brauchen wir nur eine etwas andere Kombination von
CROSS JOIN
s um auf 1.000.000 Zeilen zu kommen:;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) , -- 10e2(n) AS (WÄHLEN SIE 1 AUS e1 CROSS JOIN e1 AS b), -- 10*10e3(n) AS (WÄHLEN SIE 1 AUS e1 CROSS JOIN e2 AS b), -- 10*100e4(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;Plan:
Bei dieser Zeilengröße können Sie sehen, dass die gestapelte CTE-Lösung parallel läuft. Also habe ich auch eine Version mit
MAXDOP 1
ausgeführt um eine ähnliche Planform wie zuvor zu erhalten und um zu sehen, ob Parallelität wirklich hilft:
Rekursiver CTE
Der rekursive CTE hat wiederum nur eine geringfügige Änderung; nur das
WHERE
Klausel muss geändert werden:;MIT n(n) AS( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n <1000000)SELECT n FROM n ORDER BY nOPTION (MAXRECURSION 0);Plan:
Leistung
Wieder einmal sehen wir, dass die Leistung des rekursiven CTE miserabel ist:
Laufzeit in Millisekunden zum Generieren von 1.000.000 zusammenhängenden NummernWenn wir diesen Ausreißer aus dem Diagramm entfernen, erhalten wir ein besseres Bild über die Leistung:
Laufzeit in Millisekunden zum Generieren von 1.000.000 zusammenhängenden Zahlen (ohne rekursive CTE)Während wir erneut die unkomprimierte Numbers-Tabelle (zumindest mit einem warmen Cache) als Gewinner sehen, ist der Unterschied selbst in dieser Größenordnung nicht allzu bemerkenswert.
Fortsetzung folgt...
Nachdem wir nun eine Handvoll Ansätze zum Generieren einer Zahlenfolge gründlich untersucht haben, gehen wir zu den Daten über. Im letzten Beitrag dieser Serie gehen wir durch die Erstellung eines Datumsbereichs als Set, einschließlich der Verwendung einer Kalendertabelle, und einige Anwendungsfälle, in denen dies praktisch sein kann.
[ Teil 1 | Teil 2 | Teil 3 ]
Anhang:Zeilenanzahl
Möglicherweise versuchen Sie nicht, eine genaue Anzahl von Zeilen zu generieren. Vielleicht möchten Sie stattdessen einfach nur eine einfache Möglichkeit, viele Zeilen zu generieren. Das Folgende ist eine Liste von Kombinationen von Katalogansichten, die Ihnen verschiedene Zeilenzahlen liefern, wenn Sie einfach
SELECT
ohneWHERE
Klausel. Beachten Sie, dass diese Nummern davon abhängen, ob Sie ein RTM oder ein Service Pack verwenden (da einige Systemobjekte hinzugefügt oder geändert werden) und ob Sie eine leere Datenbank haben.
Quelle | Zeilenanzahl | ||
---|---|---|---|
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 |
Tabelle 1:Zeilenanzahl für verschiedene Katalogansichtsabfragen