web-dev-qa-db-fra.com

Mise à jour SQL CTE simple

Je suis un peu perplexe avec cette mise à jour CTE stmt:

DECLARE @a TABLE (ID int, Value int);
DECLARE @b TABLE (ID int, Value int);
INSERT @a VALUES (1, 10), (2, 20);
INSERT @b VALUES (1, 100),(2, 200);

WITH cte AS 
(
    SELECT * FROM @a
)
UPDATE cte
SET    Value = b.Value
FROM   cte AS a
INNER JOIN @b AS b 
ON     b.ID = a.ID

SELECT * FROM @a
GO

Pourquoi cela aboutit-il au tableau @a avoir 100 pour les deux rangées? Je pensais que ce devrait être 100 pour ID 1 et 200 pour ID 2.

J'obtiens les résultats attendus si j'utilise une table au lieu d'une expression de table commune pour effectuer les mises à jour:

DECLARE @a TABLE (ID int, Value int);
DECLARE @b TABLE (ID int, Value int);
INSERT @a VALUES (1, 10), (2, 20);
INSERT @b VALUES (1, 100),(2, 200);

SELECT  *
FROM    @a

UPDATE @a
SET Value = b.Value
FROM @a AS XX
INNER JOIN @b AS b ON b.ID = xx.ID

SELECT  *
FROM    @a

Il en résulte le tableau @a avec 100 et 200. Mais ne sommes-nous pas censés obtenir les deux mêmes valeurs? basé sur l'explication précédente du problème de référencement? - Mise à jour en cours vers @a table et non le XX référencé.

7
user1967701

Pour développer MguerraTorres 'answer :

(Mis à jour avec les informations de votre requête secondaire)

Dans votre première requête UPDATE cte dit de mettre à jour le tableau à partir du CTE.

FROM cte as a dit de se référer à la table du CTE comme a.

Nous avons donc fait référence à notre CTE à deux endroits.

Ce que vous ne réalisez peut-être pas, c'est qu'un CTE est réévalué à chaque fois qu'il apparaît dans votre requête, comme si vous remplaçiez la référence par une sous-requête. Puisque vous avez référencé le CTE deux fois, vous avez généré deux jeux de résultats distincts pour que le moteur de base de données fonctionne.

Lorsque vous dites d'utiliser b.Valuea.ID = b.ID, nous avons deux lignes - une où b.Value est 100, et celui où il est 200 - de la table b et de notre deuxième jeu de résultats CTE.

Cependant, nous mettons à jour le premier jeu de résultats CTE basé sur ces deux lignes. Par conséquent, il met à jour chaque ligne dans ce premier ensemble de résultats à partir des deux lignes renvoyées. Il n'y a aucune relation entre les deux jeux de résultats, même s'ils représentent les mêmes données sous-jacentes. Le moteur fait un CROSS JOIN entre les résultats de votre jointure et le premier jeu de résultats pour effectuer la mise à jour.

Votre instruction UPDATE met à jour vos deux lignes à 200, puis à 100 (car le moteur décide de la manière la plus rapide d'appliquer les lignes jointes, elles peuvent ne pas suivre l'ordre dans lequel elles ont été entrées). Les deux lignes sont mises à jour à la même valeur car elles sont mises à jour à partir des mêmes lignes multiples.

Votre première requête est fonctionnellement identique à:

DECLARE @a TABLE (ID int, Value int);
DECLARE @b TABLE (ID int, Value int);
INSERT @a VALUES (1, 10), (2, 20);
INSERT @b VALUES (1, 100),(2, 200);


WITH cte AS 
(
    SELECT * FROM @a
)
UPDATE cte
SET    Value = b.Value
FROM   (SELECT * FROM @a) AS a
INNER JOIN @b AS b 
ON     b.ID = a.ID

SELECT * FROM @a
GO

Dans votre deuxième requête, le moteur de base de données sait que a et @a référence une table en dehors de la requête, et il sait que a et @a signifie la même chose, donc il lie correctement les lignes de @b à @a lors de la mise à jour.


Dans les commentaires, vous avez demandé:

Le résultat serait-il toujours 100 pour les deux? ou peut-il parfois être de 200 pour les deux - Comme je le vois, il n'y a pas de règle claire ici?

Que ce soit 100 ou 200 peut varier.

Je dirais qu'il est probable que, étant donné les mêmes déclarations que celles présentées dans votre première requête, exécutées de la même manière, vous obtiendrez presque certainement le même résultat.

Cependant, dans le monde réel, avec des tableaux montrant d'autres activités, vous ne pouviez pas vraiment sur un résultat ou l'autre, surtout au fil du temps. Cela dépend de la façon dont le moteur de base de données correspond aux tables de la jointure, puis traite les lignes lors de l'application de la mise à jour.

10
RDFozz

Erreur simple en aliasant cte avec "a"

Vous devez mettre à jour "a" au lieu de mettre à jour "cte"

DECLARE @a TABLE (ID int, Value int);
DECLARE @b TABLE (ID int, Value int);
INSERT @a VALUES (1, 10), (2, 20);
INSERT @b VALUES (1, 100),(2, 200);


WITH cte AS (SELECT ID, Value 
         FROM @a)

  UPDATE a --Changed from "UPDATE cte"
  SET Value = b.Value
  FROM cte AS a
  INNER JOIN @b AS b ON b.ID = a.ID;



  SELECT * FROM @a;


ID          Value
----------- -----------
1           100
2           200

(2 rows affected)
9
MguerraTorres