Cảnh báo về khả năng hiển thị :Đừng trả lời khác. Nó sẽ cung cấp các giá trị không chính xác. Đọc để biết tại sao nó sai.
Cung cấp k bùn cần thiết để thực hiện UPDATE
với OUTPUT
làm việc trong SQL Server 2008 R2, tôi đã thay đổi truy vấn của mình từ:
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.BatchFileXml, inserted.ResponseFileXml, deleted.ProcessedDate
WHERE BatchReports.BatchReportGUID = @someGuid
tới:
SELECT BatchFileXml, ResponseFileXml, ProcessedDate FROM BatchReports
WHERE BatchReports.BatchReportGUID = @someGuid
UPDATE BatchReports
SET IsProcessed = 1
WHERE BatchReports.BatchReportGUID = @someGuid
Về cơ bản, tôi đã ngừng sử dụng OUTPUT
. Điều này không quá tệ như chính Entity Framework sử dụng chính bản hack này!
Hy vọng rằng 2012 2014 2016 2018 2019 Năm 2020 sẽ triển khai tốt hơn.
Cập nhật:sử dụng OUTPUT có hại
Sự cố mà chúng tôi bắt đầu gặp phải là cố sử dụng OUTPUT
mệnh đề để truy xuất "sau" giá trị trong bảng:
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.LastModifiedDate, inserted.RowVersion, inserted.BatchReportID
WHERE BatchReports.BatchReportGUID = @someGuid
Sau đó, điều đó đạt đến giới hạn biết rõ ( "sẽ không khắc phục được" lỗi) trong SQL Server:
Bảng đích 'BatchReports' của câu lệnh DML không được có bất kỳ trình kích hoạt nào được bật nếu câu lệnh chứa mệnh đề OUTPUT mà không có mệnh đề INTO
Nỗ lực Giải pháp # 1
Vì vậy, chúng tôi thử một cái gì đó mà chúng tôi sẽ sử dụng TABLE
trung gian biến để giữ OUTPUT
kết quả:
DECLARE @t TABLE (
LastModifiedDate datetime,
RowVersion timestamp,
BatchReportID int
)
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.LastModifiedDate, inserted.RowVersion, inserted.BatchReportID
INTO @t
WHERE BatchReports.BatchReportGUID = @someGuid
SELECT * FROM @t
Ngoại trừ trường hợp không thành công vì bạn không được phép chèn timestamp
vào bảng (thậm chí là một biến bảng tạm thời).
Nỗ lực Giải pháp # 2
Chúng tôi bí mật biết rằng một timestamp
thực sự là một số nguyên không dấu 64 bit (hay còn gọi là 8 byte). Chúng ta có thể thay đổi định nghĩa bảng tạm thời của mình để sử dụng binary(8)
thay vì timestamp
:
DECLARE @t TABLE (
LastModifiedDate datetime,
RowVersion binary(8),
BatchReportID int
)
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.LastModifiedDate, inserted.RowVersion, inserted.BatchReportID
INTO @t
WHERE BatchReports.BatchReportGUID = @someGuid
SELECT * FROM @t
Và điều đó hoạt động, ngoại trừ việc giá trị bị sai .
Dấu thời gian RowVersion
chúng tôi trả về không phải là giá trị của dấu thời gian vì nó đã tồn tại sau khi hoàn thành CẬP NHẬT:
- dấu thời gian được trả lại :
0x0000000001B71692
- dấu thời gian thực tế :
0x0000000001B71693
Đó là vì các giá trị OUTPUT
vào bảng của chúng tôi không các giá trị như chúng ở cuối câu lệnh UPDATE:
- Bắt đầu câu lệnh UPDATE
- sửa đổi hàng
- dấu thời gian được cập nhật (ví dụ:2 → 3)
- OUTPUT truy xuất dấu thời gian mới (tức là 3)
- kích hoạt chạy
- sửa đổi lại hàng
- dấu thời gian được cập nhật (ví dụ:3 → 4)
- sửa đổi lại hàng
- sửa đổi hàng
- Hoàn thành câu lệnh UPDATE
- OUTPUT trả về 3 (giá trị sai)
Điều này có nghĩa là:
- Chúng tôi không nhận được dấu thời gian vì nó tồn tại ở cuối câu lệnh UPDATE ( 4 )
- Thay vào đó, chúng tôi nhận được dấu thời gian như ở giữa không xác định của câu lệnh UPDATE ( 3 )
- Chúng tôi không nhận được dấu thời gian chính xác
Điều này cũng đúng với bất kỳ kích hoạt sửa đổi bất kỳ giá trị trong hàng. OUTPUT
sẽ không OUTPUT giá trị vào cuối CẬP NHẬT.
Điều này có nghĩa là bạn không thể tin tưởng OUTPUT trả lại bất kỳ giá trị chính xác nào.
Thực tế đau đớn này được ghi lại trong BOL:
Các cột được trả về từ OUTPUT phản ánh dữ liệu như cũ sau khi câu lệnh INSERT, UPDATE hoặc DELETE hoàn thành nhưng trước khi các trình kích hoạt được thực thi.
Entity Framework đã giải quyết nó như thế nào?
Khuôn khổ thực thể .NET sử dụng chuyển đổi hàng hóa cho Đồng thời Lạc quan. EF phụ thuộc vào việc biết giá trị của timestamp
vì nó tồn tại sau khi họ phát hành CẬP NHẬT.
Vì bạn không thể sử dụng OUTPUT
đối với bất kỳ dữ liệu quan trọng nào, Khung thực thể của Microsoft sử dụng cùng một cách giải quyết mà tôi thực hiện:
Giải pháp # 3 - Cuối cùng - Không sử dụng mệnh đề OUTPUT
Để truy xuất after giá trị, các vấn đề về Khung thực thể:
UPDATE [dbo].[BatchReports]
SET [IsProcessed] = @0
WHERE (([BatchReportGUID] = @1) AND ([RowVersion] = @2))
SELECT [RowVersion], [LastModifiedDate]
FROM [dbo].[BatchReports]
WHERE @@ROWCOUNT > 0 AND [BatchReportGUID] = @1
Không sử dụng OUTPUT
.
Có, nó gặp phải tình trạng chạy đua, nhưng đó là điều tốt nhất mà SQL Server có thể làm.
Còn INSERTs thì sao
Làm những gì Entity Framework làm:
SET NOCOUNT ON;
DECLARE @generated_keys table([CustomerID] int)
INSERT Customers (FirstName, LastName)
OUTPUT inserted.[CustomerID] INTO @generated_keys
VALUES ('Steve', 'Brown')
SELECT t.[CustomerID], t.[CustomerGuid], t.[RowVersion], t.[CreatedDate]
FROM @generated_keys AS g
INNER JOIN Customers AS t
ON g.[CustomerGUID] = t.[CustomerGUID]
WHERE @@ROWCOUNT > 0
Một lần nữa, họ sử dụng SELECT
để đọc hàng, thay vì đặt bất kỳ sự tin tưởng nào vào mệnh đề OUTPUT.