Làm thế nào để chèn hoặc cập nhật bằng một truy vấn duy nhất?


25

Tôi có một bài kiểm tra bảng có id cột mà khóa chính và tự động tăng và tên. Tôi muốn chèn một bản ghi mới nếu annd chỉ khi không có bản ghi. Ví dụ

đầu vào là id = 30122 và name = john

nếu có bản ghi với id 30122 thì tôi đã cập nhật cột tên thành john, nếu không có bản ghi thì tôi đã chèn một bản ghi mới.

Tôi có thể sử dụng 2 truy vấn như

select * from test where id=30122

Nếu nó có một số hồ sơ thì tôi có thể sử dụng update test set name='john' where id=3012

hoặc nếu nó không có hồ sơ thì tôi có thể sử dụng

insert into test(name) values('john')

Nhưng tôi muốn sử dụng truy vấn duy nhất?

Ai đó có thể nói nếu nó có thể?


1
But I wanted to use single query?Tại sao?
Aaron Bertrand

@AaronBertrand Phần cuối của tôi được phát triển bằng java. Vì vậy, nếu tôi sử dụng 2 thế kỷ thì tôi phải nhấn DB 2 lần. Vì vậy, nếu có thể thực hiện bằng một truy vấn duy nhất thì tại sao phải sử dụng 2 truy vấn
SpringLearner

1
Java không hỗ trợ một thủ tục được lưu trữ hoặc một lô có hai câu lệnh chỉ cần một lần truy cập vào cơ sở dữ liệu?
Aaron Bertrand

@AaronBertrand bạn có thể đưa ra một ví dụ về cách bạn sẽ xử lý việc này với máy chủ sql 2008 trở lên không?
Eaglei22

1
@ Eaglei22 Tôi sẽ sử dụng ví dụ thứ 2 trong câu trả lời của vijayp bên dưới. Tôi vẫn sẽ không chọn MERGEtrong bất kỳ phiên bản nào, ngay cả SQL Server 2019. Một số nền tảng về điều đó ở đây .
Aaron Bertrand

Câu trả lời:


41

Bạn có thể thử cái này

IF EXISTS(select * from test where id=30122)
   update test set name='john' where id=3012
ELSE
   insert into test(name) values('john');

Cách tiếp cận khác để có hiệu suất tốt hơn là

update test set name='john' where id=3012
IF @@ROWCOUNT=0
   insert into test(name) values('john');

và cũng đọc thói quen xấu này để khởi động tiền tố lược đồ


4
Ví dụ đầu tiên là lãng phí và thường có thể dẫn đến bế tắc - tôi sẽ không đề xuất gì cả.
Aaron Bertrand

@AaronBertrand quan tâm đến công phu? Cảm ơn
Hexo

5
@SlapY Chắc chắn, trong ví dụ đầu tiên, bạn đang nói: "Này, SQL Server, có một hàng với ID này không?" SQL Server tắt để tìm hàng, có thể sử dụng quét và sau đó quay lại với câu trả lời. "Tại sao, vâng, người dùng, tôi có một hàng với ID đó!" Sau đó, bạn nói, "Được rồi, SQL Server, hãy tìm lại hàng đó , nhưng lần này, hãy cập nhật nó!" Bạn có thấy việc thực hiện tìm kiếm hoặc quét hai lần là lãng phí không? Bạn có thể tưởng tượng điều gì xảy ra nếu một người dùng khác hỏi SQL Server câu hỏi tương tự về sự tồn tại của một hàng, trước khi bạn chuyển sang làm điều gì đó về nó không?
Aaron Bertrand

Cảm ơn, tôi chỉ không thấy lý do tại sao thứ nhất có nguy cơ bế tắc trong khi thứ hai thì không? Cả hai bao gồm nhiều câu lệnh có thể bị chặn nếu không chạy với khóa đầy đủ. Tôi có lầm không?
Hexo

2
@ 0x25b3 Không phải người ta bị đe dọa bởi những bế tắc và người kia thì không, đó là ví dụ đầu tiên dễ bị họ hơn nhiều. Bạn nên thực hiện một giao dịch đầy đủ và phù hợp trong cả hai trường hợp, nhưng mọi người thì không, vì vậy ...
Aaron Bertrand

17

Giả sử SQL Server 2008 trở lên, bạn có thể sử dụng MERGE:

Bàn

CREATE TABLE dbo.Test
(
    id integer NOT NULL,
    name varchar(30) NULL,

    CONSTRAINT PK_dbo_Test__id
        PRIMARY KEY CLUSTERED (id)
);

Truy vấn

MERGE dbo.Test WITH (SERIALIZABLE) AS T
USING (VALUES (3012, 'john')) AS U (id, name)
    ON U.id = T.id
WHEN MATCHED THEN 
    UPDATE SET T.name = U.name
WHEN NOT MATCHED THEN
    INSERT (id, name) 
    VALUES (U.id, U.name);

Các SERIALIZABLEgợi ý là cần thiết cho hoạt động chính xác dưới đồng thời cao .

Bạn có thể tìm thấy một so sánh về các phương pháp phổ biến của Michael J. Swart tại đây:

Mythbusting: Cập nhật đồng thời / Giải pháp chèn


8
Hợp nhất có một số vấn đề .
vonPryz

các liên kết huyền thoại có xuất sắc. Đẹp quá
JonnyRaa
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.