2010-02-17 30 views
8

Tôi có một SP trong SQL Server chạy hàng trăm lần mỗi phút và cần kiểm tra lưu lượng truy cập đến dựa vào cơ sở dữ liệu. Tại thời điểm này nó như sauTốc độ nào nhanh hơn, EXISTS trước hoặc sau INSERT?

INSERT INTO table 
SELECT @value1,@value2 WHERE NOT EXISTS 
(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2); 

Tuy nhiên, tôi cũng có thể đi với

IF NOT EXISTS(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2)  
    INSERT INTO table (value1,value2) VALUES (@value1,@value2); 

Đó sẽ là nhanh hơn? Tôi có cảm giác không có nhiều khác biệt giữa họ nhưng tôi về mặt lịch sử không giỏi về TSQL ... =/

CẬP NHẬT: Rất tiếc ... có nghĩa là nói rằng EXISTS sử dụng nhiều hơn 1 giá trị để tìm xem bản ghi tồn tại, do đó, một ràng buộc duy nhất sẽ không hoạt động. Đã chỉnh sửa mẫu để phản ánh điều đó ...

+0

http://stackoverflow.com/questions/2276023/t-sql-insert-or-update –

+0

@carlos: Đó thực sự là một câu hỏi khác, mặc dù hơi liên quan. –

+1

Lựa chọn thứ hai của bạn không an toàn. Một 'INSERT' đồng thời có thể xảy ra ở giữa các câu lệnh' IF' và 'INSERT'. – Quassnoi

Trả lời

1

Sau khi thêm một bình luận gazillion về câu hỏi này và câu trả lời của nó, tôi sẽ có của riêng tôi đi trên trả lời nó.

Tôi sẽ không mong đợi bất kỳ sự khác biệt lớn nào về hiệu suất giữa hai đề xuất được đề xuất trong câu hỏi gốc. Một mặt, như được chỉ ra bởi Ray, cách tiếp cận thứ hai có thể giúp bạn tiết kiệm một số chuẩn bị cho việc chèn, nhưng mặt khác, một RDBMS thường hoạt động tốt nhất với các lệnh lô, như trong giải pháp đầu tiên.

KM và DVK đề xuất thêm ràng buộc UNIQUE, điều này sẽ làm cho thử nghiệm độc đáo tiềm ẩn, nhưng sẽ yêu cầu bạn thêm một số loại xử lý lỗi xung quanh tuyên bố INSERT của bạn. Tôi có một thời gian khó khăn phát hiện tại sao điều này nên thêm bất kỳ hiệu suất bổ sung, giả định rằng bạn đã có một chỉ số bao gồm hai cột. Nếu bạn không có chỉ mục như vậy, hãy thêm nó và xem xét lại nhu cầu của bạn để có hiệu suất cao hơn.

Cho dù kiểm tra tính duy nhất được thực hiện rõ ràng hay ngầm thì không quan trọng AFAIK.Nếu bất cứ điều gì đạt được bằng việc kiểm tra được thực hiện "bên trong" dạ dày của DBMS, mà đạt được chỉ có thể được ăn lên bởi chi phí liên quan đến nâng cao và xử lý lỗi khi trùng lặp tồn tại.


Điểm mấu chốt: Giả sử một chỉ mục đã có sẵn, nếu bạn vẫn thấy mình thực hiện yêu cầu, đề xuất của tôi là bạn thực hiện các thử nghiệm thực nghiệm về ba giải pháp được đề xuất. Nấu một chương trình nhỏ mô phỏng dữ liệu đầu vào mong đợi và thổi từng giải pháp trong số ba giải pháp đi với vài tỷ hàng, bao gồm số lượng trùng lặp hợp lý. làm điều này, đảm bảo đăng kết quả của bạn :-)

+0

Cảm ơn, hiện tại có chỉ mục trên mỗi cột nhưng không có chỉ mục nhiều cột bao gồm cả hai. Tôi nghĩ rằng tôi sẽ đi với điều đó và thêm một ràng buộc duy nhất. Tôi không biết tôi có thể thêm một ràng buộc duy nhất bao trùm nhiều cột. – roryok

+0

@roryok: trên thực tế, bạn chỉ có thể thay đổi chỉ mục của bạn thành một 'UNIQUE INDEX', nó sẽ giống nhau. – Quassnoi

+1

Xin cảm ơn các bạn! Lần đầu tiên đăng trên StackOverflow và tôi thích nó! – roryok

0

Nếu bạn muốn các giá trị là duy nhất, tại sao không chỉ tạo một ràng buộc duy nhất trên giá trị, thực hiện INSERT mà không chọn SELECT và xử lý lỗi vi phạm ràng buộc một cách duyên dáng?

Điều đó sẽ nhanh hơn một trong các cách tiếp cận này.

Ngoài ra, cách tiếp cận đầu tiên của bạn không hoạt động - vào lúc bạn chọn, bạn đã chèn giá trị để chọn rõ ràng sẽ tìm thấy những gì bạn vừa chèn vào.

+2

Tôi tin rằng bạn đã sai trong đoạn cuối của mình. Mệnh đề 'WHERE' liên kết với' SELECT', mà thực sự được thực thi "trước" chèn. –

+0

Tôi đoán là tôi đã tạo ra lỗi StackOverflow đầu tiên rồi, tôi đã đơn giản hóa những ví dụ đó! Nó thực sự kiểm tra một bản ghi với hai giá trị để các bản ghi không phải là duy nhất ... Tôi sẽ chỉnh sửa nó để phản ánh điều đó. Tôi có thể đảm bảo 100% phương pháp đầu tiên hoạt động, tôi đã thử nghiệm nó rất nhiều lần. – roryok

+2

Việc thêm một 'ràng buộc duy nhất' kéo dài nhiều cột sẽ không thành vấn đề. –

0

Nếu tôi phải đoán, tôi đoán tùy chọn thứ hai sẽ nhanh hơn. Máy chủ sql sẽ không phải thực hiện bất kỳ loại thiết lập nào để chèn nếu tồn tại không thành công, trong khi ở đầu tiên, nó có thể tra cứu một số tên bảng và trường và chuẩn bị cho một chèn không bao giờ xảy ra. Tuy nhiên, tôi sẽ thử nó trong bộ phân tích truy vấn và xem những gì kế hoạch nói.

+0

Mặt khác, tùy chọn đầu tiên là một câu lệnh lô duy nhất, trong khi câu lệnh thứ hai là một vài câu lệnh theo kiểu thủ tục hơn. RDBMS thường rất hiệu quả với các lệnh lô, ít hơn với mã thủ tục/mệnh lệnh. Điều đó nói rằng, tôi không có ý tưởng mà trong hai báo cáo ở bàn tay thực hiện tốt nhất :) –

1

chỉ làm điều đó, và bỏ qua bất kỳ lỗi (giả định một hạn chế duy nhất trên giá trị gia tăng) ...

BEGIN TRY 
    INSERT INTO Table (value) VALUES (@value); 
END TRY 
BEGIN CATCH 
    PRINT 'it was already in there!' 
END CATCH 

Kể từ này chạy hàng trăm lần một phút, khóa gợi ý nên được thêm vào các lựa chọn và một giao dịch để avoid a race condition

(SELECT * FROM Table WITH (UPDLOCK, HOLDLOCK) WHERE value = @value); 

Tuy nhiên, ý tưởng đề xuất của tôi chỉ INSERT và bỏ qua bất kỳ lỗi khó khăn trùng lặp sẽ tránh được tình trạng chủng tộc là tốt.

+0

Cảm ơn KM, tôi đang sử dụng (NOLOCK) trên chọn vào lúc này ... nên đã đề cập rằng quá! – roryok

+0

@roryok, nếu hai cuộc gọi được thực hiện cho SQL này cùng một lúc (bạn nói rằng nó được chạy hàng trăm lần một phút) có cùng giá trị mà cả hai có thể chọn cả hàng hiện tại và cả hai đều cố gắng chèn và do đó tạo trùng lặp, đánh bại ý định của bạn –

+1

Lưu ý rằng ngoại lệ sẽ làm hỏng giao dịch nếu được nêu bên trong trình kích hoạt. – Quassnoi

3

Trong môi trường hầu như không đồng thời, INSERT đồng thời có thể xảy ra ở giữa IF NOT EXISTSINSERT trong truy vấn thứ hai của bạn.

Truy vấn đầu tiên của bạn sẽ đặt khóa chia sẻ trên bản ghi mà nó kiểm tra, sẽ không được dỡ bỏ cho đến khi kết thúc truy vấn, vì vậy sẽ không thể chèn bản ghi mới cho đến khi truy vấn đang chạy.

Tuy nhiên, bạn không nên chỉ dựa vào hành vi này. Đặt thêm UNIQUE ràng buộc trên value.

Nó sẽ không chỉ làm cho cơ sở dữ liệu phù hợp hơn, nhưng sẽ tạo ra một chỉ mục mà sẽ làm cho truy vấn đầu tiên nhanh hơn.

+2

+1 để đề cập đến nguy cơ của một điều kiện chủng tộc. –

5

Cả hai biến thể đều không chính xác. Bạn sẽ chèn các cặp trùng lặp @ value1, @ value2, đảm bảo.

Các cách chính xác để xử lý này là để thực thi một hạn chế duy nhất trên hai cột và luôn INSERT và xử lý các vi phạm chế:

ALTER TABLE Table ADD CONSTRAINT uniqueValue1Value UNIQUE (value1, values2); 

và để chèn:

BEGIN TRY 
    INSERT INTO Table (value1, value2) VALUES (@value1, @value2); 
END TRY 
BEGIN CATCH 
    DECLARE @error_number int, @error_message NVARCHAR(4000), @xact_state INT; 
    SET @error_number = ERROR_NUMBER(); 
    SET @error_message = ERROR_MESSAGE(); 
    SET @xact_state = XACT_STATE(); 
    IF (@xact_state = -1) 
    BEGIN 
    ROLLBACK TRANSACTION; 
    END 
    IF (@error_number != 2627) /* 2627 is ' Cannot insert duplicate key in object ...' */ 
    BEGIN 
     RAISERROR(N'Error inserting into Table: %i %s', 16,1, @errror_number, @error_message); 
    END 
ENd CATCH 

Trong khi những điều này có vẻ phức tạp, người ta phải tính đến một chi tiết nhỏ có tên là độ chính xác. Điều này đơn giản hơn rất nhiều khi so sánh với một giải pháp dựa trên gợi ý khóa. Đây cũng là giải pháp hiệu quả nhất: chỉ có một người tìm kiếm. Tất cả các giải pháp khác cần ít nhất hai tìm kiếm (một để xác nhận rằng nó có thể được chèn vào, một để chèn).

+0

Cảm ơn Remus. Giao dịch ROLLBACK TRANSACTION sẽ ảnh hưởng đến giao dịch nào? Toàn bộ SP được kèm theo trong một câu lệnh giao dịch bao gồm một vài câu lệnh chèn nữa, tôi không muốn kết thúc toàn bộ nếu giá trị đã có ở đó ... – roryok

+1

Khi XACT_STATE() là -1 bạn không có lựa chọn , nó có nghĩa là quá trình chuyển mã sẽ bị tiêu diệt và nó * phải * rollback. Một sự vi phạm ràng buộc duy nhất sẽ không gây ra một rollback, điều này sẽ xảy ra chỉ khi một lỗi nghiêm trọng hơn là gặp phải, như runnig ra khỏi không gian đĩa ví dụ. –

+0

Chỉ 3 năm sau cuộc thảo luận nhưng tôi đã tự hỏi về tuyên bố của bạn rằng các bản sao được đảm bảo: làm thế nào có thể INSERT ... KHÔNG PHẢI CÓ() gây ra các bản sao? Vì INSERT & SELECT là cả hai trong cùng một giao dịch, sau này mất một khóa trên bản ghi (s) và không phát hành nó cho đến khi INSERT kết thúc. Điều này cũng chặn các kết nối khác không thực hiện cùng INSERT vì chúng không thể tự khóa khóa cho đến khi quá trình chèn hoàn tất. Tôi đã làm điều này trong nhiều năm và không bao giờ có bất kỳ vấn đề với nó. AFAIK nó cũng là phương pháp hợp lệ duy nhất để sử dụng khi thực hiện chèn nhiều bản ghi. – deroby

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