Tôi có thể có tham số OUTPUT tùy chọn trong một quy trình được lưu trữ không?


77

Tôi có một thủ tục được lưu trữ có một loạt các tham số đầu vào và đầu ra vì nó đang Chèn giá trị vào nhiều bảng. Trong một số trường hợp, proc được lưu trữ chỉ chèn vào một bảng duy nhất (tùy thuộc vào các tham số đầu vào). Đây là một kịch bản giả để minh họa.

Bảng / Đối tượng dữ liệu:

Người

Id
Name
Address

Tên

Id
FirstName
LastName

Địa chỉ

Id
Country
City

Giả sử tôi có một thủ tục được lưu trữ để chèn một người. Nếu địa chỉ không tồn tại, tôi sẽ không thêm nó vào Addressbảng trong cơ sở dữ liệu.

Vì vậy, khi tôi tạo mã để gọi thủ tục được lưu trữ, tôi không muốn bận tâm thêm Addresstham số. Đối với INPUTcác tham số, điều này là ổn vì SQL Server cho phép tôi cung cấp các giá trị mặc định. Nhưng đối với OUTPUTtham số, tôi phải làm gì trong thủ tục được lưu trữ để biến nó thành tùy chọn để tôi không gặp lỗi ...

Thủ tục hoặc hàm 'Person_InsertPerson' yêu cầu tham số '@AddressId', không được cung cấp.


Mã của bạn trông như thế nào? Đó là, một nơi nào đó bạn đang phân nhánh xem địa chỉ có tồn tại hay không. Tôi nghi ngờ câu hỏi của mình, khi nhìn thấy nhánh đó, sẽ là, "Tại sao không gọi cái mầm bằng NULLbất cứ thứ gì bạn chuyển cho @AddressIdkhi một địa chỉ không tồn tại, mà không sử dụng nhánh?"
ruffin

Câu trả lời:


121

Cả hai tham số đầu vào và đầu ra đều có thể được gán giá trị mặc định. Trong ví dụ này:

CREATE PROCEDURE MyTest
  @Data1 int
 ,@Data2 int = 0
 ,@Data3 int = null output

AS

PRINT @Data1
PRINT @Data2
PRINT isnull(@Data3, -1)

SET @Data3 = @Data3 + 1

RETURN 0

tham số đầu tiên là bắt buộc, còn tham số thứ hai và thứ ba là tùy chọn - nếu không được thiết lập bởi quy trình gọi, chúng sẽ được gán các giá trị mặc định. Hãy thử làm rối với nó và quy trình cuộc gọi kiểm tra sau đây trong SSMS bằng cách sử dụng các giá trị và cài đặt khác nhau để xem cách tất cả hoạt động cùng nhau.

DECLARE @Output int

SET @Output = 3

EXECUTE MyTest
  @Data1 = 1
 ,@Data2 = 2
 ,@Data3 = @Output output

PRINT '---------'
PRINT @Output

2
Cảm ơn! Tôi đã tự hỏi tại sao tôi không thể sử dụng @var INT OUTPUT = NULL- cú pháp khó hiểu.
trojjer

2
không thực sự gọi giá trị mặc định (bạn đặt @output) giá trị mặc định chỉ kích hoạt khi bạn KHÔNG vượt qua thứ gì đó, hãy xem câu trả lời của tôi để được xử lý hoàn chỉnh.
Escape-llc,

13

Tham số đầu ra và giá trị mặc định không hoạt động tốt với nhau! Đây là từ SQL 10.50.1617 (2008 R2). Đừng để bị lừa khi tin rằng công trình này SETthay mặt bạn thực hiện một cách kỳ diệu giá trị đó (giống như đồng nghiệp của tôi đã làm)!

SP "đồ chơi" này thẩm vấn OUTPUTgiá trị tham số, cho dù đó là giá trị mặc định hay NULL.

CREATE PROCEDURE [dbo].[omgwtf] (@Qty INT, @QtyRetrieved INT = 0 OUTPUT)
AS
IF @QtyRetrieved = 0
BEGIN
    print 'yay its zero'
END
IF @QtyRetrieved is null
BEGIN
    print 'wtf its NULL'
END
RETURN

Nếu bạn gửi một giá trị chưa được khởi tạo (tức là NULL) cho OUTPUT, thì bạn thực sự có NULLbên trong SP, còn không 0. Có lý, một cái gì đó đã được chuyển cho tham số đó.

declare @QR int
exec [dbo].[omgwtf] 1, @QR output
print '@QR=' + coalesce(convert(varchar, @QR),'NULL')

đầu ra là:

wtf its NULL
@QR=NULL

Nếu chúng tôi thêm một SETtừ rõ ràng từ người gọi, chúng tôi nhận được:

declare @QR int
set @QR = 999
exec [dbo].[omgwtf] 1, @QR output
print '@QR=' + coalesce(convert(varchar, @QR),'NULL')

và đầu ra (không ngạc nhiên):

@QR=999

Một lần nữa, có lý, một tham số được truyền và SP không có hành động rõ ràng nào đối với SETmột giá trị.

Thêm một SETtrong những OUTPUTtham số trong các SP (như bạn đang phải làm), nhưng không đặt bất cứ điều gì từ người gọi:

ALTER PROCEDURE [dbo].[omgwtf] (@Qty INT, @QtyRetrieved INT = 0 OUTPUT)
AS
IF @QtyRetrieved = 0
BEGIN
    print 'yay its zero'
END
IF @QtyRetrieved is null
BEGIN
    print 'wtf its NULL'
END
SET @QtyRetrieved = @Qty
RETURN

Bây giờ khi được thực thi:

declare @QR int
exec [dbo].[omgwtf] 1234, @QR output
print '@QR=' + coalesce(convert(varchar, @QR),'NULL')

đầu ra là:

wtf its NULL
@QR=1234

Đây là hành vi "tiêu chuẩn" để OUTPUTxử lý tham số trong SP.

Bây giờ đối với cốt truyện : Cách duy nhất để lấy giá trị mặc định để "kích hoạt", là hoàn toàn không chuyển OUTPUTtham số , điều mà IMHO có chút ý nghĩa: vì nó được thiết lập như một OUTPUTtham số, điều đó có nghĩa là trả về một thứ gì đó "quan trọng" điều đó nên được thu thập.

declare @QR int
exec [dbo].[omgwtf] 1
print '@QR=' + coalesce(convert(varchar, @QR),'NULL')

cung cấp đầu ra này:

yay its zero
@QR=NULL

Nhưng điều này không thể nắm bắt được đầu ra SP, có lẽ là mục đích của SP đó để bắt đầu.

IMHO sự kết hợp tính năng này là một cấu trúc đáng ngờ mà tôi sẽ coi là một mùi mã (phew !!)


Lợi thế của việc sử dụng OUTPUTcác tham số cho đầu vào là gì (bao gồm cả việc sử dụng lại các biến trong mã có thể gọi là này và các mầm khác) ? Tại sao không chuyển các giá trị đầu vào đó dưới dạng tham số "thông thường"?
ruffin

2
@ruffin không có lợi thế; các tham số OUTPUT tùy chọn không hoạt động như mọi người mong đợi, đó là lý do tại sao tôi đã xây dựng câu trả lời này.
Escape-llc,

+1. Tôi nghĩ rằng tôi chỉ muốn nói thêm rằng điều đó có nghĩa là việc chuyển các giá trị qua các tham số OUTPUTc / sh / o nên được coi là một ý tưởng tồi vì hai lý do: 1. Ý tưởng / mùi mã, và 2. Lý do thực tế từ hành vi bạn đã tìm thấy ở đây. ¯ \ _ (ツ) _ / ¯
ruffin

Vì vậy, theo đạo lý của câu chuyện, nếu bạn định sử dụng các tham số OUTPUT tùy chọn, hãy luôn đặt giá trị mặc định thành NULL (và giữ mũi của bạn khỏi mùi).
Baodad

Trên thực tế, đạo đức là KHÔNG sử dụng hai "tính năng" đó với nhau ... giữ mũi là tùy chọn ;-)
trốn-llc

4

Có vẻ như tôi chỉ có thể thêm giá trị mặc định vào OUTPUTtham số như:

@AddressId int = -1 Output

Có vẻ như nó kém về khả năng đọc vì AddressIdnó được coi là một OUTPUTbiến số. Nhưng nó đã có tác dụng. Vui lòng cho tôi biết nếu bạn có giải pháp tốt hơn.


điều này có cạm bẫy, hãy xem câu trả lời của tôi để điều trị dứt điểm.
Escape-llc,

1

Thêm vào những gì Philip đã nói:

Tôi có một thủ tục được lưu trữ trong cơ sở dữ liệu máy chủ sql của mình trông giống như sau:

dbo.<storedProcedure>
(@current_user char(8) = NULL,
@current_phase char(3) OUTPUT)

Và tôi đã gọi nó từ mã .net của mình như sau:

 DataTable dt = SqlClient.ExecuteDataTable(<connectionString>, <storedProcedure>);

Tôi đã nhận được System.Data.SqlClient.SqlException: Thủ tục hoặc hàm yêu cầu tham số '@current_phase', không được cung cấp.

Tôi cũng đang sử dụng hàm này ở một nơi khác trong chương trình của mình và chuyển vào một tham số và xử lý đầu ra. Để tôi không phải sửa đổi cuộc gọi hiện tại mà tôi đang thực hiện, tôi chỉ cần thay đổi thủ tục được lưu trữ để làm cho tham số đầu ra cũng là tùy chọn.

Vì vậy, bây giờ nó trông như sau:

dbo.<storedProcedure>
(@current_user char(8) = NULL,
@current_phase char(3) = NULL OUTPUT)

1

Vì bạn đang thực thi một thủ tục được lưu trữ chứ không phải một câu lệnh SQL, bạn phải đặt loại lệnh của Lệnh SQL thành Thủ tục được Lưu trữ:

cmd.CommandType = CommandType.StoredProcedure;

Lấy từ đây .

Ngoài ra, khi bạn đã loại bỏ được lỗi đó, bạn có thể sử dụng nvl()hàm của SQL trong thủ tục của mình để chỉ định những gì bạn muốn hiển thị khi gặp giá trị NULL.

Xin lỗi vì không giải quyết đúng câu hỏi ... chắc bạn đã hiểu lầm. Đây là một ví dụ về nvl, mà tôi nghĩ có thể giải quyết nó tốt hơn một chút?

select NVL(supplier_city, 'n/a')
from suppliers;

Câu lệnh SQL ở trên sẽ trả về 'n / a' nếu supplier_citytrường chứa giá trị null. Nếu không, nó sẽ trả về supplier_citygiá trị.


Điều đó không trả lời câu hỏi. Tôi đang sử dụng db.GetStoredProcCommand của Thư viện Doanh nghiệp sẽ làm điều đó cho tôi. Tôi đã gọi nhiều procs được lưu trữ theo cách này và chúng hoạt động khá tốt. ví dụ. Mã để gọi StoredProc bằng cách sử dụng (DbCommand insertPersonCommand = db.GetStoredProcCommand ("Person_InsertPerson")) {this.AppendInsertPersonParameters (db, insertPersonCommand); db.ExecuteNonQuery (insertPersonCommand, dbTransaction);
Justin
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.