2009-09-27 19 views
6

Với sự giúp đỡ của những người khác trên SO Tôi đã gõ lên một vài Bàn và Thủ tục lưu trữ, sáng nay, như tôi xa một lập trình viên DB.Quy trình này có được lưu trữ an toàn không? (hoặc bất kỳ equiv là trên SQL Server)

Liệu ai đó có thể quan tâm đến vấn đề này và cho tôi biết rằng nó có an toàn không? Tôi đoán đó có lẽ không phải là thuật ngữ mà các nhà phát triển DBAs/DB sử dụng nhưng tôi hy vọng bạn nhận được ý tưởng: về cơ bản, điều gì xảy ra nếu sp này đang thực thi và một cái khác xuất hiện cùng lúc? Người ta có thể can thiệp vào người kia không? Đây có phải là vấn đề trong SQL/SP không?

CREATE PROCEDURE [dbo].[usp_NewTicketNumber] 
    @ticketNumber int OUTPUT 
AS 
BEGIN 
    SET NOCOUNT ON; 
    INSERT INTO [TEST_Db42].[dbo].[TicketNumber] 
       ([CreatedDateTime], [CreatedBy]) 
     VALUES 
       (GETDATE(), SUSER_SNAME()) 
    SELECT @ticketNumber = IDENT_CURRENT('[dbo].[TicketNumber]'); 
    RETURN 0; 
END 
+0

Tôi hoàn toàn đồng ý với câu trả lời của gbn, nhưng muốn thêm rằng bạn có thể dễ dàng tìm ra điều này - bạn chỉ có thể chạy thủ tục được lưu trữ đồng thời từ hai hoặc nhiều kết nối trong một lần mnay (> 1mln) và xem bản thân bạn. –

Trả lời

17

Bạn có lẽ không muốn được sử dụng IDENT_CURRENT - điều này sẽ trả về danh tính mới nhất được tạo ra trên các bảng trong câu hỏi, trong bất kỳ phiên và bất kỳ phạm vi. Nếu người nào khác thực hiện chèn vào sai thời điểm bạn sẽ nhận được id của họ thay thế!

Nếu bạn muốn nhận danh tính được tạo bằng chèn mà bạn vừa thực hiện thì tốt nhất bạn nên sử dụng mệnh đề OUTPUT để truy xuất nó. Nó được sử dụng bình thường để sử dụng SCOPE_IDENTITY() cho điều này nhưng có vấn đề với điều đó theo kế hoạch thực hiện song song.

Tương đương SQL chính của an toàn luồng là khi nhiều câu lệnh được thực thi gây ra hành vi không mong muốn hoặc không mong muốn. Hai loại chính của hành vi như vậy tôi có thể nghĩ đến là khóa (trong deadlocks cụ thể) và các vấn đề tương tranh.

Sự cố về khóa xảy ra khi tuyên bố dừng các câu lệnh khác truy cập vào các hàng đang hoạt động. Điều này có thể ảnh hưởng đến hiệu suất và trong kịch bản tồi tệ nhất hai câu lệnh thực hiện các thay đổi không thể được điều chỉnh và một bế tắc xảy ra, khiến một câu lệnh bị chấm dứt.

Tuy nhiên, một chèn đơn giản giống như cái bạn có không nên gây ra khóa trừ khi có điều gì đó khác liên quan (như giao dịch cơ sở dữ liệu).

Sự cố đồng thời (mô tả chúng rất kém) là do một bộ thay đổi đối với hồ sơ cơ sở dữ liệu ghi đè các thay đổi khác cho cùng một bản ghi. Một lần nữa, điều này không phải là một vấn đề khi chèn một bản ghi.

+2

+1 để đề xuất SCOPE_IDENTITY –

+1

-1 để đề xuất SCOPE_IDENTITY; http://support.microsoft.com/kb/2019779. Bạn nên sử dụng mệnh đề OUTPUT trong mọi trường hợp. –

+0

@ MichaelJ.Gray cảm ơn - Tôi đã khắc phục điều đó. Đã không nghe nói về vấn đề này trước đây. Bạn có biết khi nào nó được phát hiện? –

1

Tắt đầu tiên - tại sao bạn không chỉ trả lại số vé mới thay vì 0 mọi lúc? Bất kỳ lý do cụ thể cho điều đó?

Thứ hai, để hoàn toàn chắc chắn, bạn nên bọc câu lệnh INSERT và SELECT vào một GIAO DỊCH để không có gì từ bên ngoài can thiệp.

Thứ ba, với SQL Server 2005 trở lên, tôi muốn đưa các câu lệnh của mình vào TRY .... CATCH chặn và cuộn lại giao dịch nếu không thành công.

Tiếp theo, tôi sẽ cố gắng tránh chỉ định máy chủ cơ sở dữ liệu (TestDB42) trong quy trình của tôi bất cứ khi nào có thể - nếu bạn muốn triển khai proc đó sang máy chủ mới (TestDB43) ?? Và cuối cùng, tôi sẽ không bao giờ sử dụng SET NOCOUNT trong một thủ tục lưu sẵn - nó có thể khiến người gọi nhầm suy nghĩ được lưu trữ (xem bình luận của tôi về gbn dưới đây). Đây là một vấn đề tiềm ẩn nếu bạn đang sử dụng Chỉ đối tượng ADO.NET SqlDataAdapter, xem MSDN docs về cách sửa đổi dữ liệu ADO.NET với SqlDataAdapter để giải thích thêm).

Vì vậy, đề nghị của tôi cho proc lưu trữ của bạn sẽ là:

CREATE PROCEDURE [dbo].[usp_NewTicketNumber] 
AS 
BEGIN 
    DECLARE @NewID INT 

    BEGIN TRANSACTION 
    BEGIN TRY 
    INSERT INTO 
     [dbo].[TicketNumber]([CreatedDateTime], [CreatedBy]) 
    VALUES 
     (GETDATE(), SUSER_SNAME()) 

    SET @NewID = SCOPE_IDENTITY() 

    COMMIT TRANSACTION 
    END TRY 
    BEGIN CATCH 
    ROLLBACK TRANSACTION 
    SET @NewID = -1 
    END CATCH 

    RETURN @NewID 
END 

Marc

+0

Bạn sẽ không bao giờ sử dụng SET NOCOUNT ON? Có thật không? – gbn

+0

Vâng, nếu bạn đang sử dụng DataAdapter của ADO.NET, sử dụng SET NOCOUNT ON có thể khiến DataAdapter suy nghĩ sai lầm rằng một proc bị lưu không thành công vì nó diễn giải giá trị trả về là số "hàng bị ảnh hưởng" - và nếu nó là 0 (trong trường hợp của SET NOCOUNT ON), sau đó DataAdapter diễn giải rằng "proc đã lưu không thành công". –

+0

Bài viết này là 8 tuổi vì vậy có lẽ áp dụng cho v1.0 dot net. Tôi đã không nhìn thấy lỗi này trong nhiều năm: Tôi cho rằng nó đã được sửa vào lúc này ... – gbn

3
CREATE PROCEDURE [dbo].[usp_NewTicketNumber] 
    @NewID int OUTPUT 
AS 
BEGIN 
    SET NOCOUNT ON; 
    BEGIN TRY 
     BEGIN TRANSACTION 
     INSERT INTO 
      [dbo].[TicketNumber] ([CreatedDateTime], [CreatedBy]) 
     VALUES 
      (GETDATE(), SUSER_SNAME()) 

     SET @NewID = SCOPE_IDENTITY() 

     COMMIT TRANSACTION; 
    END TRY 
    BEGIN CATCH 
     IF XACT_STATE() <> 0 
      ROLLBACK TRANSACTION; 
     SET @NewID = NULL; 
    END CATCH 
END 

tôi sẽ không sử dụng RETURN cho dữ liệu sử dụng có ý nghĩa: hoặc recordset hoặc tham số đầu ra. RETURN thường sẽ được sử dụng cho các quốc gia lỗi (như hệ thống lưu trữ procs làm trong hầu hết các trường hợp):

EXEC @rtn = EXEC dbo.uspFoo 
IF @rtn <> 0 
    --do error stuff 

Bạn cũng có thể sử dụng mệnh đề OUTPUT để trả về một recordset để thay thế.

Đây là "chủ đề an toàn", có thể chạy đồng thời.

1

Tôi đồng ý với câu trả lời của David Hall, tôi chỉ muốn mở rộng một chút về lý do tại sao ident_current hoàn toàn là điều sai để sử dụng trong tình huống này.

Chúng tôi đã có một nhà phát triển ở đây đã sử dụng nó. Chèn từ ứng dụng máy khách xảy ra cùng lúc cơ sở dữ liệu đã nhập hàng triệu bản ghi thông qua nhập tự động. Các id trở lại với anh ta là từ một trong những hồ sơ quá trình của tôi nhập khẩu. Ông đã sử dụng id này để tạo hồ sơ cho một số bảng con mà bây giờ được gắn vào hồ sơ sai. Tồi tệ hơn, chúng ta không biết bao nhiêu lần điều này xảy ra trước khi ai đó không thể tìm thấy thông tin cần có trong bảng con (sự thay đổi của anh ta đã được sản xuất trong vài tháng). Không chỉ việc nhập tự động của tôi có thể can thiệp vào mã của anh ta, nhưng một người dùng khác chèn một bản ghi vào thời điểm đó có thể đã làm điều tương tự. Ident_current sẽ không bao giờ được sử dụng để trả về danh tính của bản ghi vừa được chèn vào vì nó không bị giới hạn trong quá trình gọi nó.

+0

Đó là chính xác loại điều tôi đã cố gắng tránh vì vậy cảm ơn rất nhiều vì đã thêm nó. – serialhobbyist

7

Cách an toàn nhất để đến đây có thể là sử dụng mệnh đề Đầu ra, vì có lỗi đã biết trong scope_idendity trong một số trường hợp nhất định (xử lý đa/song song).

CREATE PROCEDURE [dbo].[usp_NewTicketNumber] 
AS 
BEGIN 
    DECLARE @NewID INT 

    BEGIN TRANSACTION 
    BEGIN TRY 
    declare @ttIdTable TABLE (ID INT) 
    INSERT INTO 
     [dbo].[TicketNumber]([CreatedDateTime], [CreatedBy]) 
    output inserted.id into @ttIdTable(ID) 
    VALUES 
     (GETDATE(), SUSER_SNAME()) 

    SET @NewID = (SELECT id FROM @ttIdTable) 

    COMMIT TRANSACTION 
    END TRY 
    BEGIN CATCH 
    ROLLBACK TRANSACTION 
    SET @NewID = -1 
    END CATCH 

    RETURN @NewID 
END 

Bằng cách này, bạn nên là chủ đề an toàn, vì điều khoản đầu ra sử dụng dữ liệu chèn thực tế và bạn sẽ không gặp vấn đề gì trên phạm vi hoặc phiên.

+0

Liên kết đến MSKB scope_identity: http://support.microsoft.com/default.aspx?scid=kb;en-US;2019779 –

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