it-swarm.com.de

Einfaches SQL CTE-Update

Ich bin ein wenig ratlos über dieses CTE-Update:

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

Warum führt dies zu Tabelle @a 100 für beide Zeilen? Ich dachte, es sollte 100 für ID 1 und 200 für ID 2 sein.

Ich erhalte erwartete Ergebnisse, wenn ich für die Aktualisierungen eine Tabelle anstelle eines allgemeinen Tabellenausdrucks verwende:

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

Dies führt zu Tabelle @a mit 100 und 200. Aber sollen wir nicht beide die gleichen Werte erhalten? basierend auf der vorherigen Erklärung des Referenzierungsproblems? - Aktualisierung auf @a Tabelle und nicht die referenzierte XX.

7
user1967701

So erweitern Sie MguerraTorres 'Antwort :

(Aktualisiert mit den Informationen aus Ihrer sekundären Abfrage)

In Ihrer ersten Abfrage sagt UPDATE cte, Die Tabelle vom CTE zu aktualisieren.

FROM cte as a Sagt, die Tabelle aus dem CTE als a zu bezeichnen.

Wir haben uns also an zwei Stellen auf unseren CTE bezogen.

Was Sie möglicherweise nicht bemerken, ist, dass ein CTE jedes Mal neu bewertet wird, wenn er in Ihrer Abfrage angezeigt wird, als ob Sie die Referenz durch eine Unterabfrage ersetzt hätten. Da Sie den CTE zweimal getrennt referenziert haben, haben Sie zwei separate Ergebnismengen generiert, mit denen die DB-Engine arbeiten kann.

Wenn Sie sagen, Sie sollen b.Value Verwenden, wobei a.ID = b.ID, Haben wir zwei Zeilen - eine, in der b.Value 100 ist, und eine, in der es 200 ist - aus Tabelle b und aus unserer zweiten CTE-Ergebnismenge.

Wir aktualisieren jedoch die CTE-Ergebnismenge first basierend auf diesen beiden Zeilen. Daher wird jede Zeile in dieser ersten Ergebnismenge aus den beiden zurückgegebenen Zeilen aktualisiert. Es gibt keine Beziehung zwischen den beiden Ergebnismengen, obwohl sie dieselben zugrunde liegenden Daten darstellen. Die Engine führt einen CROSS JOIN Zwischen den Ergebnissen Ihres Joins und der ersten Ergebnismenge durch, um die Aktualisierung durchzuführen.

Ihre UPDATE -Anweisung aktualisiert beide Zeilen auf 200 und dann auf 100 (da die Engine entscheidet, wie die gekreuzten Zeilen am schnellsten angewendet werden sollen, werden sie möglicherweise nicht in der Reihenfolge abgelegt, in der sie eingegeben wurden). Beide Zeilen werden auf denselben Wert aktualisiert, da sie aus denselben mehreren Zeilen aktualisiert werden.

Ihre erste Abfrage ist funktional identisch mit:

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

In Ihrer zweiten Abfrage weiß die DB-Engine, dass sowohl a als auch @a Auf eine Tabelle außerhalb der Abfrage verweisen, und sie weiß, dass a und @a Die bedeuten Das Gleiche gilt für die korrekte Verknüpfung der Zeilen von @b mit @a, wenn das Update durchgeführt wird.


In den Kommentaren haben Sie gefragt:

Wäre das Ergebnis für beide immer 100? oder kann es manchmal 200 für beide sein - Wie ich sehe, gibt es hier keine klare Regel?

Ob es 100 oder 200 ist, kann variieren.

Ich würde sagen, dass es wahrscheinlich ist, dass Sie mit den gleichen Aussagen, die in Ihrer ersten Abfrage gezeigt wurden und auf die gleiche Weise ausgeführt wurden, mit ziemlicher Sicherheit das gleiche Ergebnis erzielen würden.

In der realen Welt, in der Tabellen andere Aktivitäten sehen, konnte man jedoch nicht wirklich das eine oder andere Ergebnis erzielen, insbesondere im Laufe der Zeit. Dies hängt davon ab, wie die DB-Engine mit den Tabellen im Join übereinstimmt und dann die Zeilen beim Anwenden des Updates verarbeitet.

10
RDFozz

Einfacher Fehler beim Aliasing von cte mit "a"

Sie sollten "a" aktualisieren, anstatt "cte" zu aktualisieren.

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