Database
 sql >> Baza danych >  >> RDS >> Database

Generuj zestaw lub sekwencję bez pętli – część 2

W moim poprzednim poście mówiłem o sposobach generowania sekwencji ciągłych liczb od 1 do 1000. Teraz chciałbym omówić kolejne poziomy skali:generowanie zestawów po 50 000 i 1 000 000 liczb.

Generowanie zestawu 50 000 liczb

Rozpoczynając tę ​​serię, byłem naprawdę ciekawy, jak różne podejścia będą skalować się do większych zestawów liczb. Na dole byłem trochę przerażony, gdy stwierdziłem, że to moje ulubione podejście – użycie sys.all_objects – nie była najskuteczniejszą metodą. Ale w jaki sposób te różne techniki skalują się do 50 000 wierszy?

    Tabela liczb

    Ponieważ utworzyliśmy już tabelę liczb z 1 000 000 wierszy, to zapytanie pozostaje praktycznie identyczne:

    SELECT TOP (50000) n FROM dbo.Numbers ORDER BY n;

    Plan:

    spt_values

    Ponieważ w spt_values jest tylko ~ 2500 wierszy , musimy być trochę bardziej kreatywni, jeśli chcemy użyć go jako źródła naszego generatora zestawów. Jednym ze sposobów symulowania większej tabeli jest CROSS JOIN to przeciwko sobie. Gdybyśmy zrobili to na surowo, skończylibyśmy z ~2500 wierszami do kwadratu (ponad 6 milionów). Potrzebujemy tylko 50 000 rzędów, potrzebujemy około 224 rzędów do kwadratu. Możemy to zrobić:

    ;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 y
    ORDER BY n;

    Zwróć uwagę, że jest to równoważne, ale bardziej zwięzłe niż ta odmiana:

    SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.number) 
    FROM 
    (SELECT TOP (224) number FROM [master]..spt_values) AS x
    CROSS JOIN
    (SELECT TOP (224) number FROM [master]..spt_values) AS y
    ORDER BY n;

    W obu przypadkach plan wygląda tak:

    sys.all_objects

    Podobnie jak spt_values , sys.all_objects samo w sobie nie do końca spełnia nasze wymagania dotyczące 50 000 wierszy, więc będziemy musieli wykonać podobne CROSS JOIN .

    ;;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:

    Skumulowane CTE

    Musimy tylko dokonać niewielkiej korekty naszych skumulowanych CTE, aby uzyskać dokładnie 50 000 wierszy:

    ;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
    ), -- 10
    e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10
    e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100
    e4(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:

    Rekursywne CTE

    Jeszcze mniej istotna zmiana jest wymagana, aby uzyskać 50 000 wierszy z naszego rekurencyjnego CTE:zmień WHERE klauzulę na 50 000 i zmień MAXRECURSION opcja na zero.

    ;WITH n(n) AS
    (
        SELECT 1
        UNION ALL
        SELECT n+1 FROM n WHERE n < 50000
    )
    SELECT n FROM n ORDER BY n
    OPTION (MAXRECURSION 0);

    Plan:

    W tym przypadku pojawia się ikona ostrzegawcza – jak się okazuje, w moim systemie rodzaj musiała się rozlać do tempdb. Możesz nie zobaczyć wycieku w systemie, ale powinno to być ostrzeżeniem o zasobach wymaganych do tej techniki.

    Wydajność

    Podobnie jak w przypadku ostatniego zestawu testów, porównamy każdą technikę, w tym tabelę Numbers z zimną i ciepłą pamięcią podręczną, zarówno skompresowaną, jak i nieskompresowaną:


    Uruchom w milisekundach, aby wygenerować 50 000 ciągłych liczb

    Aby uzyskać lepszy obraz, usuńmy rekurencyjne CTE, które w tym teście było całkowitym psem i które zniekształca wyniki:


    Uruchom w milisekundach, aby wygenerować 50 000 ciągłych liczb (z wyłączeniem rekurencyjnych CTE)

    Przy 1000 wierszach różnica między skompresowanymi a nieskompresowanymi była marginalna, ponieważ zapytanie musiało odczytać odpowiednio tylko 8 i 9 stron. Przy 50 000 wierszy luka nieco się powiększa:74 strony w porównaniu do 113. Jednak całkowity koszt dekompresji danych wydaje się przewyższać oszczędności we/wy. Tak więc, mająca 50 000 wierszy, nieskompresowana tabela liczb wydaje się być najskuteczniejszą metodą wyprowadzania ciągłego zestawu – choć, trzeba przyznać, przewaga jest marginalna.

Generowanie zestawu 1 000 000 liczb

Chociaż nie wyobrażam sobie zbyt wielu przypadków użycia, w których potrzebny byłby tak duży ciągły zestaw liczb, chciałem go uwzględnić dla kompletności, a także ponieważ dokonałem kilku interesujących obserwacji w tej skali.

    Tabela liczb

    Żadnych niespodzianek, nasze zapytanie brzmi teraz:

    SELECT TOP 1000000 n FROM dbo.Numbers ORDER BY n;

    TOP nie jest bezwzględnie konieczne, ale dzieje się tak tylko dlatego, że wiemy, że nasza tabela liczb i pożądany wynik mają tę samą liczbę wierszy. Plan jest nadal dość podobny do poprzednich testów:

    spt_values

    Aby uzyskać CROSS JOIN co daje 1 000 000 wierszy, musimy wziąć 1000 wierszy do kwadratu:

    ;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

    Ponownie potrzebujemy iloczynu krzyżowego 1000 wierszy:

    ;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:

    Skumulowane CTE

    W przypadku skumulowanego CTE potrzebujemy tylko nieco innej kombinacji CROSS JOIN s, aby dostać się do 1 000 000 wierszy:

    ;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
    ), -- 10
    e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10
    e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2 AS b), -- 10*100
    e4(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:

    Przy tym rozmiarze wiersza widać, że skumulowane rozwiązanie CTE działa równolegle. Uruchomiłem więc również wersję z MAXDOP 1 aby uzyskać podobny kształt planu jak poprzednio i sprawdzić, czy równoległość naprawdę pomaga:

    Rekursywny CTE

    Rekursywne CTE ponownie ma tylko niewielką zmianę; tylko WHERE klauzula musi ulec zmianie:

    ;WITH n(n) AS
    (
        SELECT 1
        UNION ALL
        SELECT n+1 FROM n WHERE n < 1000000
    )
    SELECT n FROM n ORDER BY n
    OPTION (MAXRECURSION 0);

    Plan:

    Wydajność

    Po raz kolejny widzimy, że wydajność rekurencyjnego CTE jest fatalna:


    Uruchom w milisekundach, aby wygenerować 1 000 000 ciągłych liczb

    Usuwając tę ​​wartość odstającą z wykresu, uzyskujemy lepszy obraz wydajności:


    Czas uruchomieniowy w milisekundach w celu wygenerowania 1 000 000 ciągłych liczb (z wyłączeniem rekurencyjnych CTE)

    Podczas gdy ponownie widzimy nieskompresowaną tabelę liczb (przynajmniej z ciepłą pamięcią podręczną) jako zwycięzcę, różnica nawet w tej skali nie jest aż tak znacząca.

Ciąg dalszy…

Teraz, gdy dokładnie zbadaliśmy kilka podejść do generowania sekwencji liczb, przejdziemy do dat. W ostatnim poście z tej serii omówimy konstrukcję zakresu dat jako zestawu, w tym użycie tabeli kalendarza oraz kilka przypadków użycia, w których może to być przydatne.

[ Część 1 | Część 2 | Część 3 ]

Dodatek:Liczby wierszy

Być może nie próbujesz wygenerować dokładnej liczby wierszy; możesz zamiast tego po prostu chcieć prostego sposobu generowania wielu wierszy. Poniżej znajduje się lista kombinacji widoków katalogu, które dadzą Ci różne liczby wierszy, jeśli po prostu SELECT bez WHERE klauzula. Pamiętaj, że te liczby będą zależeć od tego, czy jesteś w RTM, czy w dodatku Service Pack (ponieważ niektóre obiekty systemowe są dodawane lub modyfikowane), a także od tego, czy masz pustą bazę danych.

Źródło Liczba wierszy
SQL Server 2008 R2 SQL Server 2012 SQL Server 2014
master..spt_values

2508

2515 2519
master..spt_values ​​POŁĄCZENIE KRZYŻOWE master..spt_values

6 290 064

6 325 225 6 345 361
sys.all_objects

1990

2089 2165
sys.all_columns

5157

7276 8,560
sys.all_objects POŁĄCZENIE KRZYŻOWE sys.all_objects

3960100

4363 921 4687225
sys.all_objects POŁĄCZENIE KRZYŻOWE sys.all_columns

10 262 430

15 199 564 18 532 400
sys.all_columns POŁĄCZENIE KRZYŻOWE sys.all_columns

26 594 649

52 940 176 73 273 600

Tabela 1:Liczby wierszy dla różnych zapytań o widok katalogu


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Przekształć dane ODBC w CloverDX

  2. Operator SQL nie jest równy (!=) dla początkujących

  3. Synchronizacja struktury bazy danych między aplikacjami

  4. 10 przydatnych zasobów dla tych, którzy chcą dowiedzieć się więcej o SQL

  5. KLUCZ OBCY SQL