2010-02-16 42 views
27

Tôi có một câu hỏi liên quan đến hiệu suất của SQL Server.T-SQL Chèn hoặc cập nhật

Giả sử tôi có một bảng persons với các cột sau: id, name, surname.

Bây giờ, tôi muốn chèn một hàng mới trong bảng này. Quy tắc như sau:

  1. Nếu không có mặt số id trong bảng, sau đó chèn hàng.

  2. Nếu có id, sau đó cập nhật.

Tôi có hai giải pháp ở đây:

Đầu tiên:

update persons 
    set [email protected]_id, [email protected]_name, [email protected]_surname 
where [email protected]_id 
if @@ROWCOUNT = 0 
    insert into persons(id, name, surname) 
    values (@p_id, @p_name, @p_surname) 

Thứ hai:

if exists (select id from persons where id = @p_id) 
    update persons 
    set [email protected]_id, [email protected]_name, [email protected]_surname 
    where [email protected]_id 
else 
    insert into persons(id, name, surname) 
    values (@p_id, @p_name, @p_surname) 

một cách tiếp cận tốt hơn là gì? Nó có vẻ như trong sự lựa chọn thứ hai, để cập nhật một hàng, nó phải được tìm kiếm hai lần, trong khi trong tùy chọn đầu tiên - chỉ một lần. Có giải pháp nào khác cho vấn đề này không? Tôi đang sử dụng MS SQL 2000.

+1

không chắc chắn, nhưng tôi sẽ chỉ làm nếu ((COUNT (*))> 0) sau đó cập nhật tùy chọn thứ hai –

Trả lời

7

Cả hai làm việc tốt, nhưng tôi thường sử dụng phương án 2 (pre-MSSQL 2008) kể từ khi nó đọc một chút rõ ràng hơn. Tôi sẽ không căng thẳng về hiệu suất ở đây hoặc là ... Nếu nó trở thành một vấn đề, bạn có thể sử dụng NOLOCK trong mệnh đề exists. Mặc dù trước khi bạn bắt đầu sử dụng NOLOCK ở khắp mọi nơi, hãy chắc chắn rằng bạn đã bao phủ tất cả các cơ sở của bạn (chỉ mục và các công cụ kiến ​​trúc hình ảnh lớn). Nếu bạn biết bạn sẽ cập nhật mọi mục nhiều lần, thì có thể trả tiền để xem xét tùy chọn 1.

Tùy chọn 3 không sử dụng cập nhật phá hoại. Nó đòi hỏi nhiều công việc hơn, nhưng về cơ bản bạn chèn một hàng mới mỗi khi dữ liệu thay đổi (không bao giờ cập nhật hoặc xóa khỏi bảng) và có một khung nhìn chọn tất cả các hàng gần đây nhất. Sẽ rất hữu ích nếu bạn muốn bảng chứa lịch sử của tất cả các trạng thái trước đó của nó, nhưng nó cũng có thể quá mức cần thiết.

11

Tùy chọn 1 có vẻ tốt. Tuy nhiên, nếu bạn đang sử dụng SQL Server 2008, bạn cũng có thể sử dụng MERGE, có thể hoạt động tốt cho các nhiệm vụ UPSERT đó.

Lưu ý rằng bạn có thể muốn sử dụng giao dịch rõ ràng và tùy chọn XACT_ABORT cho các tác vụ như vậy, để duy trì tính nhất quán của giao dịch trong trường hợp có sự cố hoặc thay đổi đồng thời.

4

Tôi có xu hướng sử dụng tùy chọn 1. Nếu có bản ghi trong bảng, bạn lưu một tìm kiếm. Nếu không có, bạn không mất gì cả. Hơn nữa, trong tùy chọn thứ hai, bạn có thể chạy vào khóa buồn cười và các vấn đề bế tắc liên quan đến khóa không tương thích. Có một số thông tin thêm về blog của tôi:

http://sqlblogcasts.com/blogs/piotr_rodak/archive/2010/01/04/updlock-holdlock-and-deadlocks.aspx

2

Nhắm đến một chút DRY hơn, tôi tránh viết ra danh sách giá trị hai lần.

begin tran 
insert into persons (id) 
select @p_id from persons 
where not exists (select * from persons where id = @p_id) 

update persons 
set [email protected]_name, [email protected]_surname 
where id = @p_id 

commit 

Cột namesurname phải được vô hiệu.

Giao dịch có nghĩa là không người dùng nào khác sẽ thấy bản ghi "trống".

Chỉnh sửa: dọn dẹp

+1

Thanh lịch nhưng bạn cũng sẽ phải chèn giá trị cho mỗi không trống trong bảng. –

+0

Đúng. Tôi có xu hướng tránh sử dụng chức năng của 'không null'. Các ứng dụng cần phải được khá bung được tải lên nulls cho các lĩnh vực quan trọng – Patrick

+0

Giải pháp cuối cùng của tôi cuối cùng là để làm logic trong PHP, cho 'put' - cố gắng chèn đầy đủ, và nếu nó không với' $ sqlErrors [0] [1] == 2627', tôi cập nhật đầy đủ – Patrick

0

Bạn chỉ có thể sử dụng @@ RowCount để xem bản cập nhật có làm gì không. Một cái gì đó như:

Các vấn đề liên quan