web-dev-qa-db-fra.com

Différence entre la vue en ligne et la clause WITH?

Les vues en ligne vous permettent de sélectionner à partir d'une sous-requête comme s'il s'agissait d'une table différente:

SELECT
    *
FROM /* Selecting from a query instead of table */
    (
        SELECT
            c1
        FROM
            t1
        WHERE
            c1 > 0
    ) a
WHERE
    a.c1 < 50;

J'ai vu que cela faisait référence à l'utilisation de différents termes: vues en ligne, clause WITH, CTE et tables dérivées. Pour moi, il semble que ce soit une syntaxe spécifique au fournisseur différente pour la même chose.

Est-ce une fausse hypothèse? Existe-t-il des différences techniques/de performances entre ces derniers?

9
Kshitiz Sharma

Il existe des différences importantes entre les vues en ligne (tables dérivées) et la clause WITH (CTE) dans Oracle. Certains d'entre eux sont assez universels, c'est-à-dire qu'ils s'appliquent à d'autres SGBDR.

  1. WITH peut être utilisé pour créer des sous-requêtes récursives, une vue en ligne -pas (pour autant que je sache, c'est la même chose pour tous les SGBDR qui prennent en charge CTE)
  2. La sous-requête dans la clause WITH est plus susceptible d'être exécutée physiquement en premier; dans de nombreux cas, le choix entre WITH et la vue en ligne permet à l'optimiseur de choisir différents plans d'exécution (je suppose que c'est spécifique au fournisseur, peut-être même à la version).
  3. La sous-requête dans WITH peut être matérialisée comme une table temporaire (je ne sais pas si un autre fournisseur mais Oracle prend en charge cette fonctionnalité).
  4. La sous-requête dans WITH peut être référencée plusieurs fois, dans d'autres sous-requêtes et dans la requête principale (vrai pour la plupart des SGBDR).
8
a1ex07

D'autres réponses couvrent assez bien les différences de syntaxe, donc je n'entrerai pas dans les détails. Au lieu de cela, cette réponse couvrira simplement les performances dans Oracle.

L'optimiseur Oracle peut choisir de matérialiser les résultats d'un CTE dans une table temporaire interne. Il utilise une heuristique pour ce faire au lieu d'une optimisation basée sur les coûts. L'heuristique est quelque chose comme "Matérialiser le CTE si ce n'est pas une expression triviale et le CTE est référencé plus d'une fois dans la requête". Il existe certaines requêtes pour lesquelles la matérialisation améliorera les performances. Il existe certaines requêtes pour lesquelles la matérialisation dégradera considérablement les performances. L'exemple suivant est un peu artificiel mais il illustre bien le point:

Créez d'abord une table avec une clé primaire qui contient des entiers de 1 à 10000:

CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));

INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;

COMMIT;

Considérez la requête suivante qui utilise deux tables dérivées:

SELECT t1.NUM_ID
FROM 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Nous pouvons examiner cette requête et déterminer rapidement qu'elle ne renverra aucune ligne. Oracle devrait également pouvoir utiliser l'index pour le déterminer. Sur ma machine, la requête se termine presque instantanément avec le plan suivant:

good plan

Je n'aime pas me répéter, alors essayons la même requête avec un CTE:

WITH N_10000_CTE AS (
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Voici le plan:

bad plan

C'est vraiment un mauvais plan. Au lieu d'utiliser l'index, Oracle matérialise 10000 X 10000 = 100000000 lignes dans une table temporaire pour finalement retourner 0 lignes. Le coût de ce plan est d'environ 6 M, ce qui est beaucoup plus élevé que l'autre requête. La requête a pris 68 secondes pour se terminer sur ma machine.

Notez que la requête aurait pu échouer s'il n'y avait pas suffisamment de mémoire ou d'espace libre dans l'espace disque logique temporaire.

Je peux utiliser l'astuce INLINE non documentée pour empêcher l'optimiseur de matérialiser le CTE:

WITH N_10000_CTE AS (
  SELECT /*+ INLINE */ n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Cette requête est capable d'utiliser l'index et se termine presque instantanément. Le coût de la requête est le même qu'avant, 11. Donc pour la deuxième requête, l'heuristique utilisée par Oracle l'a amené à choisir une requête avec un coût estimé à 6 M au lieu d'une requête avec un coût estimé à 11.

8
Joe Obbish

Pour SQL Server, WITH CTE spécifie le jeu de résultats nommé temporaire, mais n'est requis que pour le premier CTE. c'est à dire.

WITH CTE AS (SELECT .... FROM), 
CTE2 AS (SELECT .... FROM)

SELECT CTE.Column, CTE2.Column
FROM CTE
INNER JOIN CTE2 on CTE.Column = CTE2.Column

Mais ce n'est pas une sous-requête ou une sous-requête corrélée. Il y a des choses que vous pouvez faire avec un CTE ce que vous ne pouvez pas faire avec une sous-requête dans SQL Server, comme mettre à jour les tables référencées dans un CTE. Voici un exemple de mise à jour d'une table avec un CTE.

Une sous-requête serait quelque chose comme

SELECT
   C1,
   (SELECT C2 FROM SomeTable) as C2
FROM Table

Ou une sous-requête corrélée est ce que vous avez fourni dans votre PO si vous deviez référencer/joindre/limiter vos résultats en fonction de a.c1.

Donc, ce n'est certainement pas la même chose, bien que dans de nombreux cas, vous puissiez utiliser une ou plusieurs de ces méthodes pour obtenir le même résultat. Cela dépend simplement de ce que ce résultat final est.

1
scsimon

La principale différence entre la clause with et une sous-requête dans Oracle est que vous pouvez référencer plusieurs fois une requête dans la clause. Vous pouvez ensuite faire quelques optimisations avec lui comme le transformer en une table temporaire en utilisant l'astuce materialize. Vous pouvez également effectuer des requêtes récursives avec lui en se référençant à l'intérieur d'une clause with. Vous ne pouvez pas faire cela avec une vue en ligne.

Plus d'informations peuvent être trouvées ici et ici .

1
Marko Vodopija

Vous devez être prudent avec les CTE dans SQL Server et pas seulement Oracle, il y a des cas où les requêtes fonctionnent bien moins lors de l'utilisation des CTE par rapport aux sous-requêtes, aux applications croisées, etc.

Comme toujours, il est important de tester toute requête dans différentes conditions de charge pour déterminer celle qui fonctionne le mieux.

Semblable à @scsimon avec Oracle, parfois MS SQL Server ne fait pas ce que vous attendez en ce qui concerne l'utilisation des index.

Si vous allez utiliser les mêmes données plus d'une fois, les CTE peuvent être plus utiles, si vous ne les utilisez qu'une seule fois, souvent une sous-requête est plus rapide dans les grands ensembles de données.

par exemple. sélectionner * dans (ma sous-requête) rejoindre autre chose ...

0
Justin