Database
 sql >> Base de données >  >> RDS >> Database

Générer un ensemble ou une séquence sans boucles – partie 2

Dans mon article précédent, j'ai parlé des moyens de générer une séquence de nombres contigus de 1 à 1 000. J'aimerais maintenant parler des prochains niveaux d'échelle :générer des ensembles de 50 000 et 1 000 000 nombres.

Générer un ensemble de 50 000 numéros

Au début de cette série, j'étais vraiment curieux de savoir comment les différentes approches s'adapteraient à de plus grands ensembles de nombres. Au bas de l'échelle, j'étais un peu consterné de constater que mon approche préférée - utiliser sys.all_objects – n'était pas la méthode la plus efficace. Mais comment ces différentes techniques évolueraient-elles jusqu'à 50 000 lignes ?

    Tableau des nombres

    Comme nous avons déjà créé une table Numbers avec 1 000 000 de lignes, cette requête reste pratiquement identique :

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

    Forfait :

    spt_values

    Puisqu'il n'y a que ~2 500 lignes dans spt_values , nous devons être un peu plus créatifs si nous voulons l'utiliser comme source de notre générateur de set. Une façon de simuler une table plus grande consiste à CROSS JOIN elle contre elle-même. Si nous faisions cela brut, nous nous retrouverions avec ~ 2 500 lignes au carré (plus de 6 millions). N'ayant besoin que de 50 000 lignes, nous avons besoin d'environ 224 lignes au carré. Nous pouvons donc faire ceci :

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

    Notez que cela équivaut à cette variation, mais en plus concis :

    SELECT TOP (50000) n =ROW_NUMBER() OVER (ORDER BY x.number) FROM (SELECT TOP (224) nombre FROM [maître]..spt_values) AS xCROSS JOIN(SELECT TOP (224) nombre FROM [maître ]..spt_values) AS yORDER BY n;

    Dans les deux cas, le plan ressemble à ceci :

    sys.all_objects

    Comme spt_values , sys.all_objects ne satisfait pas tout à fait notre exigence de 50 000 lignes à elle seule, nous devrons donc effectuer un CROSS JOIN similaire .

    ;;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 PAR n ;

    Forfait :

    CTE empilés

    Nous n'avons qu'à apporter un ajustement mineur à nos CTE empilés afin d'obtenir exactement 50 000 lignes :

    ;AVEC e1(n) AS( CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 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 5 TOP n FROM e1) AS b) -- 5*10000 SELECT n =ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;

    Forfait :

    CTE récursifs

    Un changement encore moins substantiel est nécessaire pour extraire 50 000 lignes de notre CTE récursif :modifiez le WHERE clause à 50 000 et modifiez le MAXRECURSION option à zéro.

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

    Forfait :

    Dans ce cas, il y a une icône d'avertissement sur le tri - il s'avère que, sur mon système, le tri devait se répandre sur tempdb. Vous ne verrez peut-être pas de déversement sur votre système, mais cela devrait être un avertissement sur les ressources requises pour cette technique.

    Performances

    Comme pour la dernière série de tests, nous comparerons chaque technique, y compris la table Numbers avec un cache froid et chaud, compressé et non compressé :


    Durée d'exécution, en millisecondes, pour générer 50 000 nombres contigus

    Pour obtenir un meilleur visuel, supprimons le CTE récursif, qui était un chien total dans ce test et qui fausse les résultats :


    Durée d'exécution, en millisecondes, pour générer 50 000 nombres contigus (à l'exclusion CTE)

    À 1 000 lignes, la différence entre compressé et non compressé était marginale, puisque la requête n'avait besoin de lire que 8 et 9 pages respectivement. A 50 000 lignes, l'écart se creuse un peu :74 pages contre 113. Cependant, le coût global de la décompression des données semble l'emporter sur les gains en E/S. Ainsi, à 50 000 lignes, une table de nombres non compressés semble être la méthode la plus efficace pour dériver un ensemble contigu - bien que, certes, l'avantage soit marginal.

Générer un ensemble de 1 000 000 numéros

Bien que je ne puisse pas imaginer de très nombreux cas d'utilisation où vous auriez besoin d'un ensemble contigu de nombres aussi grand, je voulais l'inclure par souci d'exhaustivité et parce que j'ai fait des observations intéressantes à cette échelle.

    Tableau des nombres

    Pas de surprise ici, notre requête est maintenant :

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

    Le TOP n'est pas strictement nécessaire, mais c'est uniquement parce que nous savons que notre table Numbers et notre sortie souhaitée ont le même nombre de lignes. Le plan est quand même assez similaire aux tests précédents :

    spt_values

    Pour obtenir un CROSS JOIN qui donne 1 000 000 lignes, nous devons prendre 1 000 lignes au carré :

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

    Forfait :

    sys.all_objects

    Encore une fois, nous avons besoin du produit croisé de 1 000 lignes :

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

    Forfait :

    CTE empilés

    Pour le CTE empilé, nous avons juste besoin d'une combinaison légèrement différente de CROSS JOIN s pour atteindre 1 000 000 lignes :

    ;AVEC e1(n) AS( CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1 UNION TOUT CHOISIR 1) , -- 10e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10e3(n) AS (SELECT 1 FROM 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;

    Forfait :

    À cette taille de ligne, vous pouvez voir que la solution CTE empilée est parallèle. J'ai donc également exécuté une version avec MAXDOP 1 pour obtenir une forme de plan similaire à celle d'avant, et pour voir si le parallélisme aide vraiment :

    CTE récursif

    Le CTE récursif n'a à nouveau qu'un changement mineur; seulement le WHERE la clause doit changer :

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

    Forfait :

    Performances

    Une fois de plus, nous constatons que les performances du CTE récursif sont catastrophiques :


    Durée d'exécution, en millisecondes, pour générer 1 000 000 nombres contigus

    En supprimant cette valeur aberrante du graphique, nous obtenons une meilleure image des performances :


    Durée d'exécution, en millisecondes, pour générer 1 000 000 nombres contigus (à l'exclusion CTE)

    Alors qu'une fois de plus, nous voyons la table Numbers non compressée (au moins avec un cache chaud) comme la gagnante, la différence, même à cette échelle, n'est pas si remarquable.

A suivre…

Maintenant que nous avons exploré en profondeur une poignée d'approches pour générer une séquence de nombres, nous allons passer aux dates. Dans le dernier article de cette série, nous allons parcourir la construction d'une plage de dates en tant qu'ensemble, y compris l'utilisation d'un tableau de calendrier, et quelques cas d'utilisation où cela peut être pratique.

[ Partie 1 | Partie 2 | Partie 3 ]

Annexe :nombre de lignes

Vous n'essayez peut-être pas de générer un nombre exact de lignes ; vous pouvez plutôt vouloir simplement un moyen simple de générer beaucoup de lignes. Ce qui suit est une liste de combinaisons de vues de catalogue qui vous permettront d'obtenir différents nombres de lignes si vous faites simplement SELECT sans WHERE clause. Notez que ces nombres dépendront de si vous êtes sur un RTM ou un service pack (puisque certains objets système sont ajoutés ou modifiés), et aussi si vous avez une base de données vide.

Source Le nombre de lignes
SQL Server 2008 R2 SQL Server 2012 SQL Server 2014
maître..spt_values

2 508

2 515 2 519
maître..spt_values ​​CROSS JOIN maître..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

Tableau 1 :Nombre de lignes pour diverses requêtes d'affichage du catalogue