2009-10-10 35 views
12

Tôi có một bảng:T-SQL UPDATE có điều kiện (v2)

Message (MessageID int, Subject nvarchar(100), Body nvarchar(max)) 

Sau một tin nhắn đang được cập nhật trên giao diện người dùng, tôi gọi một proc lưu trữ để cập nhật bảng đó. Trong một số trường hợp, người dùng có thể cập nhật chỉ chủ đề, trong các trường hợp khác chỉ là nội dung. Tôi muốn proc lưu trữ này để chỉ cập nhật những gì đã thay đổi, vì vậy tôi cũng đang đi cờ cho thấy cho dù chủ đề hoặc cơ thể đã được cập nhật:

create proc UpdateMessage(
    @MessageID int, 
    @Subject nvarchar(100), 
    @Body nvarchar(max), 
    @SubjectChanged bit, 
    @BodyChanged bit) 

Và bây giờ tôi đang bối rối như thế nào để xây dựng UPDATE tuyên bố có điều kiện. Suy nghĩ đầu tiên của tôi là sử dụng CASE:

Update [Message] 
SET 
CASE WHEN @SubjectChanged = 1 THEN [Subject] = @Subject ELSE 1=1 END, 
CASE WHEN @BodyChanged = 1 THEN Body = @Body ELSE 1=1 END, 
WHERE MessageID = @MessageID 

... nhưng điều đó dường như không phải là một cú pháp đúng như CASE phải là phía bên phải của một assigment.

Bất kỳ ý tưởng nào về cách tôi có thể làm điều đó? (Và hãy nhớ rằng trong thực tế có 6 thông số có thể được cập nhật, không phải là hai)

Trả lời

24

Cú pháp cần thiết để tạo tuyên bố của bạn là:

Update [Message] 
SET [Subject] = CASE WHEN @SubjectChanged = 1 THEN @Subject ELSE [Subject] END, 
     Body = CASE WHEN @BodyChanged = 1 THEN @Body ELSE Body END 
WHERE MessageID = @MessageID 

nếu bạn vẫn muốn dính vào nó sau khi tất cả những lời đề nghị.

N.b. nếu bạn bỏ phần ELSE [Subject] của các câu lệnh CASE, thay vì bỏ qua UPDATE nó sẽ đặt trường thành NULL.

1

Dường như với tôi như bạn đang lãng phí rất nhiều công sức. Nếu bạn lấy sáu giá trị, hiển thị chúng cho người dùng (trong một số giao diện người dùng) và họ có thể thay đổi một số biến số của chúng và nhấn nút "lưu" - sau đó chỉ cập nhật tất cả 6 trường mọi lúc, nhận các giá trị mới từ các trường nhập của người dùng.

Một số có thể không thay đổi, nhưng vì vậy, những gì. Mã đơn giản hơn nhiều theo cách đó.

+0

Xin lưu ý rằng nội dung thư có thể là 5-10-50kb và nếu người dùng vừa cập nhật chủ đề, sẽ vô cùng lãng phí khi gửi nội dung thư và cũng thực hiện cập nhật của trường đó cho db khi không cần thiết. Trang web tải nặng nên tôi cần đảm bảo tối ưu hóa khi có thể. – Andrey

+0

Tại sao không chỉ sử dụng các thủ tục lưu trữ riêng biệt? Liệu nó thực sự cần phải được bao hàm trong một sproc duy nhất? Có thể bạn sẽ muốn thực hiện một số sửa đổi trong tương lai sẽ hủy hoại hoàn toàn "kế hoạch tuyệt vời" –

+1

"Tại sao không chỉ sử dụng các thủ tục được lưu trữ riêng biệt?" - để lưu vào nhiều cuộc gọi db – Andrey

0

Sử dụng giá trị DEFAULT cho các tham số thủ tục được lưu trữ.

create proc UpdateMessage(
    @MessageID int, -- mandatory 
    @Subject nvarchar(100) = NULL, 
    @Body nvarchar(max) = NULL) 

Sau đó, bạn có thể cấu trúc cập nhật của bạn theo cách này:

Update [Message] 
SET 
[Subject] = ISNULL(@Subject, [Subject]), 
Body = ISNULL(@Body, Body) 
WHERE MessageID = @MessageID 
+3

Điều gì sẽ xảy ra nếu bạn đang cố cập nhật trường thành NULL? –

+0

Và cũng vậy, khi @Body là NULL, tôi đang cập nhật một trường có giá trị riêng, đang lãng phí các tài nguyên có giá trị (ví dụ: nếu Message.Body là 1 Meg thì đó là bản cập nhật khá nặng và không cần thiết). – Andrey

6
update Message set 
    Subject = (case when @SubjectChanged = 1 then @Subject else Subject end), 
    Body = (case when @BodyChanged = 1 then @Body else Body end) 

where MessageID = @MessageID 

Đó thực sự phải là tất cả các bạn cần. Tuy nhiên, nếu bạn thực sự không thể cập nhật trường nếu nó không thay đổi, thì bạn sẽ phải làm điều đó trong các câu lệnh riêng biệt.

if @SubjectChanged = 1 
    update Message set Subject = @Subject where MessageID = @MessageID 
if @BodyChanged = 1 
    update Message set Body = @Body where MessageID = @MessageID 
0

Tôi không chắc chắn nếu điều này là cách tốt nhất để làm điều đó, nhưng có lẽ bạn có thể thử

IF @SubjectChanged = 1 THEN 
    BEGIN 
     UPDATE [Message] 
     SET [Subject] = @Subject 
     WHERE MessageID = @MessageID  
    END 
END 

IF @BodyChanged = 1 THEN 
    BEGIN 
     UPDATE [Message] 
     SET Body = @Body 
     WHERE MessageID = @MessageID 
    END 
END 
6

Tốt nhất, cho đến nay, là để sử dụng rõ ràng IF báo cáo:

IF @subjectHasChanged = 1 and @bodyHasChanged = 1 
UPDATE Messages SET Subject = @subject, Body = @body 
    WHERE MessageId = @MessageId 
ELSE IF @subjectHasChanged = 1 
UPDATE Messages SET Subject = @subject WHERE MessageId = @MessageId 
ELSE IF @bodyHasChanged 
UPDATE Messages SET Body = @body WHERE MessageId = @MessageId 

Từ quan điểm hiệu suất, không có gì đánh bại điều này. Bởi vì SQL có thể thấy trong quá trình biên dịch truy vấn mà bạn chỉ cập nhật Body, hoặc Subject, hoặc cả hai, nó có thể tạo ra kế hoạch thích hợp, ví dụ như không bận tâm để mở (để cập nhật) chỉ mục không nhóm mà bạn có trên Subject (nếu bạn có một, tất nhiên) khi bạn chỉ cập nhật nội dung.

Từ một điểm mã chất lượng, đây là thảm họa, một cơn ác mộng để duy trì. Nhưng thừa nhận vấn đề là 80% giải quyết vấn đề :). Bạn có thể sử dụng các kỹ thuật tạo mã ví dụ để duy trì các thủ tục vấn đề như vậy.

Một cách tiếp cận khả thi khác là sử dụng SQL động, xây dựng UPDATE trong quy trình và sử dụng sp_executesql.Nó có tập hợp các vấn đề riêng của nó, vì tất cả các SQL động đều có. Có các tài nguyên về các vấn đề SQL động, và có giải pháp và giải pháp, xem The Curse and Blessings of Dynamic SQL.

0

Tôi rất muốn khuyên bạn nên sử dụng phương pháp của Adam Robinson nếu bạn yêu cầu điều này là trong một thủ tục được lưu trữ duy nhất.

Thậm chí tốt hơn sẽ chỉ đơn giản là sử dụng các thủ tục được lưu trữ riêng biệt cho mỗi một trong các bản cập nhật này.