Exemple de requête en parallèle

La requête suivante compte le nombre de commandes passées dans le courant du trimestre débutant le 1er avril 2000, dont au moins un poste a été livré au client à une date postérieure à la date prévue. Cette requête affiche le nombre de ce type de commandes groupées par priorité de commande et triées en ordre de priorité croissant.

Cet exemple utilise des noms de tables et de colonnes théoriques.

SELECT o_orderpriority, COUNT(*) AS Order_Count
FROM orders
WHERE o_orderdate >= '2000/04/01'
   AND o_orderdate < DATEADD (mm, 3, '2000/04/01')
   AND EXISTS
         (
          SELECT *
            FROM    lineitem
            WHERE l_orderkey = o_orderkey
               AND l_commitdate < l_receiptdate
         )
   GROUP BY o_orderpriority
   ORDER BY o_orderpriority

Supposons que les index suivants soient définis dans les tables lineitem et orders :

CREATE INDEX l_order_dates_idx 
   ON lineitem
      (l_orderkey, l_receiptdate, l_commitdate, l_shipdate)

CREATE UNIQUE INDEX o_datkeyopr_idx
   ON ORDERS
      (o_orderdate, o_orderkey, o_custkey, o_orderpriority)

Voici un plan d'exécution en parallèle possible, généré pour la requête illustrée précédemment :

|--Stream Aggregate(GROUP BY:([ORDERS].[o_orderpriority])
                  DEFINE:([Expr1005]=COUNT(*)))
    |--Parallelism(Gather Streams, ORDER BY:
                  ([ORDERS].[o_orderpriority] ASC))
         |--Stream Aggregate(GROUP BY:
                  ([ORDERS].[o_orderpriority])
                  DEFINE:([Expr1005]=Count(*)))
              |--Sort(ORDER BY:([ORDERS].[o_orderpriority] ASC))
                   |--Merge Join(Left Semi Join, MERGE:
                  ([ORDERS].[o_orderkey])=
                        ([LINEITEM].[l_orderkey]),
                  RESIDUAL:([ORDERS].[o_orderkey]=
                        [LINEITEM].[l_orderkey]))
                        |--Sort(ORDER BY:([ORDERS].[o_orderkey] ASC))
                        |    |--Parallelism(Repartition Streams,
                           PARTITION COLUMNS:
                           ([ORDERS].[o_orderkey]))
                        |         |--Index Seek(OBJECT:
                     ([tpcd1G].[dbo].[ORDERS].[O_DATKEYOPR_IDX]),
                     SEEK:([ORDERS].[o_orderdate] >=
                           Apr  1 2000 12:00AM AND
                           [ORDERS].[o_orderdate] <
                           Jul  1 2000 12:00AM) ORDERED)
                        |--Parallelism(Repartition Streams,
                     PARTITION COLUMNS:
                     ([LINEITEM].[l_orderkey]),
                     ORDER BY:([LINEITEM].[l_orderkey] ASC))
                             |--Filter(WHERE:
                           ([LINEITEM].[l_commitdate]<
                           [LINEITEM].[l_receiptdate]))
                                  |--Index Scan(OBJECT:
         ([tpcd1G].[dbo].[LINEITEM].[L_ORDER_DATES_IDX]), ORDERED)

Plan de requête avec DOP 4 qui implique une jointure à deux tables

L'illustration montre un plan d'optimiseur de requête exécuté avec un degré de parallélisme de 4 et comprenant une jointure entre deux tables.

Le plan en parallèle comprend trois opérateurs Parallelism. L'opérateur Index Seek de l'index o_datkey_ptr et l'opérateur Index Scan de l'index l_order_dates_idx sont exécutés en parallèle, ce qui produit plusieurs flux exclusifs. Cela peut être déterminé à partir des opérateurs Parallelism les plus proches des opérateurs Index Scan et Index Seek, respectivement. Dans les deux cas, le type d'échange est répartitionné. En d'autres termes, les données sont tout simplement distribuées aux flux en produisant le même nombre de flux en sortie qu'en entrée. Ce nombre de flux est égal au degré de parallélisme.

L'opérateur Parallelism qui se trouve au-dessus de l'opérateur Index Seek de l'index l_order_dates_idx répartitionne ses flux d'entrée en utilisant la valeur de L_ORDERKEY en tant que clé. De cette façon, toutes les valeurs identiques de L_ORDERKEY se retrouvent dans un même flux de sortie. Au même moment, les flux de sortie gèrent l'ordre de la colonne L_ORDERKEY afin qu'elle réponde aux conditions d'entrée de l'opérateur Merge Join.

L'opérateur Parallelism qui se trouve au-dessus de l'opérateur Index Seek répartitionne ses flux d'entrée en utilisant la valeur de O_ORDERKEY. Étant donné que son entrée n'est pas triée dans les valeurs de la colonne O_ORDERKEY et qu'il s'agit de la colonne de jointure de l'opérateur Merge Join, l'opérateur Sort qui se trouve entre les opérateurs Parallelism et Merge Join s'assure que l'entrée est triée pour l'opérateur Merge Join dans les colonnes de jointure. L'opérateur Sort, tout comme l'opérateur Merge Join, est exécuté en parallèle.

Le premier opérateur Parallelism rassemble les résultats de plusieurs flux en un seul flux. Les agrégations partielles effectuées par l'opérateur Stream Aggregate situé sous l'opérateur Parallelism sont ensuite accumulées dans une seule valeur SUM pour chaque valeur différente de O_ORDERPRIORITY dans l'opérateur Stream Aggregate qui se trouve au-dessus de l'opérateur Parallelism. Étant donné que le plan comporte deux segments d'échange avec un degré de parallélisme de 4, il utilise huit threads.