Database
 sql >> Datenbank >  >> RDS >> Database

Erstellen Sie ein Set oder eine Sequenz ohne Schleifen – Teil 2

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 Nummern

    Wenn 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 ohne WHERE 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