SQL Server 2005 triển khai MySQL REPLACE INTO?


86

MySQL có REPLACE INTOLệnh SQL cực kỳ hữu ích nhưng độc quyền này .

Điều này có thể dễ dàng được mô phỏng trong SQL Server 2005 không?

Bắt đầu một Giao dịch mới, thực hiện một Select()và sau đó hoặc UPDATEhoặc INSERTCOMMITluôn là một chút khó khăn, đặc biệt là khi thực hiện nó trong ứng dụng và do đó luôn giữ 2 phiên bản của câu lệnh.

Tôi tự hỏi liệu có cách nào dễ dàng và phổ biến để triển khai một hàm như vậy vào SQL Server 2005 không?

Câu trả lời:


60

Đây là điều gì đó làm tôi khó chịu về MSSQL ( rant trên blog của tôi ). Tôi muốn MSSQL được hỗ trợ upsert.

Mã của @ Dillie-O là một cách hay trong các phiên bản SQL cũ hơn (+1 phiếu bầu), nhưng về cơ bản nó vẫn là hai hoạt động IO ( existsvà sau đó là updatehoặc insert)

Có một cách tốt hơn một chút về bài đăng này , về cơ bản:

--try an update
update tablename 
set field1 = 'new value',
    field2 = 'different value',
    ...
where idfield = 7

--insert if failed
if @@rowcount = 0 and @@error = 0
    insert into tablename 
           ( idfield, field1, field2, ... )
    values ( 7, 'value one', 'another value', ... )

Điều này làm giảm nó thành một hoạt động IO nếu đó là một bản cập nhật hoặc hai nếu một phần chèn.

MS Sql2008 giới thiệu mergetừ tiêu chuẩn SQL: 2003:

merge tablename as target
using (values ('new value', 'different value'))
    as source (field1, field2)
    on target.idfield = 7
when matched then
    update
    set field1 = source.field1,
        field2 = source.field2,
        ...
when not matched then
    insert ( idfield, field1, field2, ... )
    values ( 7,  source.field1, source.field2, ... )

Bây giờ nó thực sự chỉ là một hoạt động IO, nhưng mã khủng khiếp :-(


Cảm ơn rất nhiều! Lưu Lựa chọn và thường thậm chí không cần giao dịch thay thế trong các tình huống mà tôi có thể chắc chắn rằng giữa Bản cập nhật và phần chèn "của tôi", không có phần chèn nào khác cho khóa đó.
Michael Stum

2
@Michael Bạn nên có một chỉ mục duy nhất trên bảng này và xử lý các lỗi chính trùng lặp nếu bạn định sử dụng giải pháp này.
Sam Saffron

3
@Keith Câu lệnh hợp nhất của bạn không hoạt động. MERGEkhông hỗ trợ WHEREmệnh đề, bạn phải viết lại điều đó bằng cách sử dụng USINGON. Ngoài ra, trừ khi bạn thêm WITH (HOLDLOCK), sẽ có một cuộc đua và đồng thời INSERTcó thể xảy ra, với một trong số chúng thất bại do cuộc đụng độ quan trọng.
Evgeniy Berezovsky

Có, như đã chỉ ra ở đây: weblogs.sqlteam.com/dang/archive/2009/01/31/… MERGE không phải là nguyên tử. Nó lấy ra một khóa cập nhật ngầm, nhưng giải phóng nó trước khi thực hiện chèn, điều này gây ra tình trạng chạy đua có thể dẫn đến vi phạm khóa chính. Bạn phải sử dụng HOLDLOCK rõ ràng ngoài UPDLOCK ngầm định để hoạt động là nguyên tử. Như hiện tại, nó không phải là nguyên tử, mặc dù có vẻ là một tuyên bố duy nhất.
Triynko

1
Cú pháp MERGE sai và nó được sửa trong một câu trả lời gần đây hơn từ cùng một tác giả: stackoverflow.com/a/243670/24472
Larry

21

Chức năng bạn đang tìm kiếm thường được gọi là UPSERT. Ít nhất biết nó được gọi là gì có thể giúp bạn tìm thấy những gì bạn đang tìm kiếm.

Tôi không nghĩ SQL Server 2005 có bất kỳ cách tuyệt vời nào để thực hiện việc này. 2008 giới thiệu câu lệnh MERGE có thể được sử dụng để thực hiện điều này như được hiển thị trong: http://www.databasejournal.com/features/mssql/article.php/3739131 hoặc http://blogs.conchango.com/davidportas/archive/ 2007/11/14 / SQL-Server-2008-MERGE.aspx

Merge có sẵn trong bản beta năm 2005, nhưng họ đã loại bỏ nó trong bản phát hành cuối cùng.


18

Những gì upert / merge đang làm là một cái gì đó có hiệu lực của ...

IF EXISTS (SELECT * FROM [Table] WHERE Id = X)
   UPDATE [Table] SET...
ELSE
   INSERT INTO [Table]

Vì vậy, hy vọng sự kết hợp của những bài báo đó và mã giả này có thể giúp mọi thứ tiến triển.


10

Tôi đã viết một bài blog về vấn đề này.

Điểm mấu chốt là nếu bạn muốn cập nhật giá rẻ và muốn an toàn khi sử dụng đồng thời, hãy thử:

update t
set hitCount = hitCount + 1
where pk = @id

if @@rowcount < 1 
begin 
   begin tran
      update t with (serializable)
      set hitCount = hitCount + 1
      where pk = @id
      if @@rowcount = 0
      begin
         insert t (pk, hitCount)
         values (@id,1)
      end
   commit tran
end

Bằng cách này, bạn có 1 thao tác cho các bản cập nhật và tối đa 3 thao tác cho các lần chèn. Vì vậy, nếu bạn thường cập nhật, đây là một lựa chọn giá rẻ an toàn.

Tôi cũng sẽ rất cẩn thận để không sử dụng bất kỳ thứ gì không an toàn để sử dụng đồng thời. Việc vi phạm khóa chính hoặc các hàng trùng lặp trong sản xuất thực sự dễ dàng.

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.