Чи можу я отримати необов’язковий параметр OUTPUT у збереженій процедурі?


77

У мене є збережена процедура, яка має купу вхідних та вихідних параметрів, оскільки це Вставка значень до декількох таблиць. У деяких випадках збережений процес вставляється лише в одну таблицю (залежно від вхідних параметрів). Ось макетний сценарій для ілюстрації.

Таблиці / Об'єкти даних:

Людина

Id
Name
Address

Ім'я

Id
FirstName
LastName

Адреса

Id
Country
City

Скажімо, у мене є збережена процедура, яка вставляє людину. Якщо адреса не існує, я не буду додавати її до Addressтаблиці в базі даних.

Таким чином, коли я генерую код для виклику збереженої процедури, я не хочу турбуватися додаванням Addressпараметра. Що стосується INPUTпараметрів, це нормально, оскільки SQL Server дозволяє мені надавати значення за замовчуванням. Але для OUTPUTпараметра, що я роблю в збереженій процедурі, він стає необов’язковим, щоб я не отримував помилку ...

Процедура або функція 'Person_InsertPerson' очікує параметра '@AddressId', який не був наданий.


Як виглядає ваш код? Тобто, десь ви розгалужуєтесь, чи існує адреса. Мені здається підозрілим, коли я побачив цю гілку, моє запитання було б: "Чому б не викликати sproc з NULLтим, що ви передаєте, @AddressIdколи адреса не існує, без використання гілки?"
jorffin

Відповіді:


121

Як вхідним, так і вихідним параметрам можуть бути призначені за замовчуванням. У цьому прикладі:

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

перший параметр необхідний, а другий і третій є необов’язковими - якщо це не встановлено в режимі виклику, їм буде присвоєно значення за замовчуванням. Спробуйте возитися з ним і з наступною процедурою тестового виклику в SSMS, використовуючи різні значення та налаштування, щоб побачити, як це все працює разом.

DECLARE @Output int

SET @Output = 3

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

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

2
Дякую! Мені було цікаво, чому я не можу використовувати @var INT OUTPUT = NULL- синтаксис заплутаний.
trojjer

2
насправді не викликає значення за замовчуванням (ви встановлюєте @output) за замовчуванням активується лише тоді, коли ви НЕ передаєте щось, див. мою відповідь для повного лікування.
escape-llc

13

Вихідні параметри та значення за замовчуванням погано працюють разом! Це з SQL 10.50.1617 (2008 R2). Не обманюйте себе, вважаючи, що ця конструкція магічно робить це SETзначення від вашого імені (як це зробив мій колега)!

Цей "іграшковий" ІП запитує OUTPUTзначення параметра, незалежно від того, є воно значенням за замовчуванням або 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

Якщо ви надсилаєте неініціалізоване значення (тобто NULL) для OUTPUT, ви дійсно потрапили NULLвсередину SP, а ні 0. Має сенс, для цього параметра щось передано.

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

вихід:

wtf its NULL
@QR=NULL

Якщо ми додамо явний текст SETвід абонента, то отримаємо:

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

і (що не дивно) результат:

@QR=999

Знову ж таки, це має сенс, параметр передається, і SP не робив явних дій зі SETзначенням.

Додати SETв OUTPUTпараметрі в SP (як ви повинні робити), але нічого від абонента не встановлені:

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

Тепер при виконанні:

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

Вихід:

wtf its NULL
@QR=1234

Це "стандартна" поведінка для OUTPUTобробки параметрів в SP.

Тепер щодо сюжету : Єдиний спосіб отримати значення за замовчуванням для "активації" - це взагалі не передавати OUTPUTпараметр , що IMHO мало сенсу: оскільки він налаштований як OUTPUTпараметр, це означало б повернення чогось "важливого" що слід зібрати.

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

дає цей результат:

yay its zero
@QR=NULL

Але це не вдається зафіксувати вихідні дані SP, мабуть, мета цього SP для початку.

ІМХО ця комбінація функцій - сумнівна конструкція, яку я вважав би запахом коду (ну !!)


Яка перевага у порівнянні зі стислістю (включаючи повторне використання змінних у коді, які можуть викликати цей та інші sprocs) у використанні OUTPUTпараметрів для введення? Чому б не передати ці вхідні значення як "звичайні" параметри?
jorffin

2
@ruffin немає переваг; необов’язкові параметри ВИХІД не працюють, як очікують усі, саме тому я створив цю відповідь.
escape-llc

+1. Думаю, я просто додам, що це означає, що передавання значень за допомогою OUTPUTпараметрів c / sh / ould вважається поганою ідеєю з двох причин: 1. Ідеологічний запах / запах коду та 2. Практичні причини поведінки, яку ви знайшли тут. ¯ \ _ (ツ) _ / ¯
краф

Отже, морально, якщо ви збираєтеся використовувати необов’язкові параметри ВИХІД, завжди встановлюйте значення за замовчуванням НУЛЬ (і не тримайте ніс від запаху).
Баодад

Насправді морально НЕ використовуйте ці дві "особливості" разом ... тримання за ніс необов'язкове ;-)
escape-llc

4

Схоже, я можу просто додати значення OUTPUTпараметра за замовчуванням до такого параметра, як:

@AddressId int = -1 Output

Здається, це погано з точки зору читабельності, оскільки AddressIdвоно призначене суто як OUTPUTзмінна. Але це працює. Будь ласка, повідомте мене, якщо у вас є краще рішення.


у цьому є підводні камені, див. мою відповідь для повного лікування.
escape-llc

1

Додаючи до того, що сказав Філіп:

У мене була збережена процедура в моїй базі даних SQL-сервера, яка виглядала так:

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

І я закликав це зі свого коду .net наступним чином:

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

Я отримував System.Data.SqlClient.SqlException: Процедура або функція очікує параметра '@current_phase', який не був наданий.

Я також використовую цю функцію десь у своїй програмі і передаю параметр і обробляю вихідний. Щоб мені не довелося змінювати поточний виклик, який я робив, я просто змінив збережену процедуру, щоб зробити вихідний параметр також необов’язковим.

Тож тепер це виглядає так:

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

1

Оскільки ви виконуєте збережену процедуру, а не оператор SQL, вам потрібно встановити тип команди вашої команди SQL на Збережена процедура:

cmd.CommandType = CommandType.StoredProcedure;

Взято звідси .

Крім того, як тільки ви видалите цю помилку, ви можете використовувати nvl()функцію SQL у своїй процедурі, щоб вказати, що ви хочете відображати, коли зустрічається значення NULL.

Вибачте за неправильне вирішення питання ... мабуть, вас неправильно зрозуміли. Ось приклад nvl, який, на мою думку, може вирішити його трохи краще?

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

Вищезазначений SQL-оператор повертає 'n / a', якщо supplier_cityполе містить нульове значення. В іншому випадку це поверне supplier_cityзначення.


Це не відповідає на питання. Я використовую бібліотеку підприємств db.GetStoredProcCommand, яка зробить це за мене. Я закликав багато збережених процесів таким чином, і вони працюють досить добре. напр. Код для виклику StoredProc за допомогою (DbCommand insertPersonCommand = db.GetStoredProcCommand ("Person_InsertPerson")) {this.AppendInsertPersonParameters (db, insertPersonCommand); db.ExecuteNonQuery (insertPersonCommand, dbTransaction);
Джастін,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.