Cập nhật CTE SQL đơn giản


7

Tôi hơi bối rối với bản cập nhật CTE này 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

Tại sao điều này dẫn đến bảng @acó 100 cho cả hai hàng? Tôi nghĩ rằng nó phải là 100 cho ID 1 và 200 cho ID 2.

Tôi nhận được kết quả mong đợi nếu tôi sử dụng bảng thay vì biểu thức bảng chung để thực hiện cập nhật:

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

Điều này dẫn đến bảng @acó 100 và 200. Nhưng chúng ta không nên có cả hai giá trị giống nhau sao? dựa trên lời giải thích trước đây về vấn đề tham khảo? - Cập nhật được thực hiện vào @abảng và không phải là XX được tham chiếu.

Câu trả lời:


10

Để mở rộng câu trả lời của MguerraTorres :

(Cập nhật với thông tin từ truy vấn phụ của bạn)

Trong truy vấn đầu tiên của bạn UPDATE ctenói để cập nhật bảng từ CTE.

FROM cte as anói để chỉ bảng từ CTE như a.

Vì vậy, chúng tôi đã đề cập đến CTE của chúng tôi ở hai nơi.

Điều bạn có thể không nhận ra là CTE được đánh giá lại cho mỗi lần nó xuất hiện trong truy vấn của bạn, giống như khi bạn thay thế tham chiếu bằng truy vấn con. Vì bạn đã tham chiếu CTE hai lần riêng biệt, bạn đã tạo hai kết quả riêng biệt để công cụ DB hoạt động.

Khi bạn nói sử dụng b.Valueở đâu a.ID = b.ID, chúng tôi có hai hàng - một hàng b.Valuelà 100 và một ở đó là 200 - từ bảng bvà từ kết quả CTE thứ hai của chúng tôi.

Tuy nhiên, chúng tôi đang cập nhật tập kết quả CTE đầu tiên dựa trên hai hàng này. Do đó, nó cập nhật từng hàng trong tập kết quả đầu tiên từ hai hàng được trả về. Không có mối quan hệ giữa hai kết quả, mặc dù chúng đại diện cho cùng một dữ liệu cơ bản. Công cụ đang thực hiện CROSS JOINgiữa kết quả tham gia của bạn và kết quả đầu tiên để thực hiện cập nhật.

UPDATETuyên bố của bạn cập nhật cả hai hàng của bạn thành 200, sau đó thành 100 (vì công cụ quyết định cách nhanh nhất để áp dụng các hàng được nối chéo, chúng có thể không đi theo thứ tự mà chúng được nhập). Cả hai hàng được cập nhật về cùng một giá trị vì chúng được cập nhật từ cùng một hàng.

Truy vấn đầu tiên của bạn giống hệt về chức năng:

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

Trong truy vấn thứ hai của bạn, động cơ DB biết rằng cả hai a@atham khảo một bảng bên ngoài truy vấn, và nó biết rằng a@aý nghĩa giống nhau, vì vậy nó gắn một cách chính xác các hàng từ @bđể @akhi thực hiện cập nhật.


Trong các ý kiến, bạn hỏi:

Kết quả sẽ luôn là 100 cho cả hai? hoặc có thể là 200 cho cả hai đôi khi - Theo tôi thấy không có quy tắc rõ ràng ở đây?

Cho dù đó là 100 hay 200 có thể thay đổi.

Tôi sẽ nói rằng có khả năng, với các câu lệnh tương tự được hiển thị trong truy vấn đầu tiên của bạn, được thực hiện theo cùng một cách, bạn gần như chắc chắn sẽ nhận được kết quả tương tự.

Tuy nhiên, trong thế giới thực, với các bảng nhìn thấy hoạt động khác, bạn không thể thực sự về kết quả này hay kết quả khác, đặc biệt là theo thời gian. Nó sẽ phụ thuộc vào cách công cụ DB khớp với các bảng trong phép nối và sau đó xử lý các hàng trong việc áp dụng bản cập nhật.


1
Xem bài đăng blog cũ này của Hugo Kornelis: Hãy từ chối CẬP NHẬT TỪ!
ypercubeᵀᴹ

9

Lỗi đơn giản trong việc đặt bí danh cte với "a"

Bạn nên cập nhật "a" thay vì cập nhật "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)

Cảm ơn, đây là trợ giúp. Nhưng bạn có thể giải thích tại sao cái ban đầu tạo ra cả 100 cho bảng @a không?
dùng1967701

Ma thuật? Không nghiêm túc vì bạn đang nhầm lẫn trình phân tích cú pháp bằng cách sử dụng CẬP NHẬT TỪ và không đặt bí danh chính xác. Bạn sẽ không thể chạy "CHỌN cte. * Từ cte AS a" vì bạn đang đặt bí danh cho bảng của mình với a, vì vậy điều này không khác lắm.
MguerraTorres 14/03/18
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.