web-dev-qa-db-fra.com

Puis-je avoir un paramètre OUTPUT facultatif dans une procédure stockée?

J'ai une procédure stockée qui a un tas de paramètres d'entrée et de sortie car elle insère des valeurs dans plusieurs tables. Dans certains cas, le proc stocké n'est inséré que dans une seule table (en fonction des paramètres d'entrée). Voici un scénario simulé pour illustrer.

Tables/objets de données:

Personne

Id
Name
Address

Nom

Id
FirstName
LastName

Adresse

Id
Country
City

Disons que j'ai une procédure stockée qui insère une personne. Si l'adresse n'existe pas, je ne l'ajouterai pas à la table Address de la base de données.

Ainsi, lorsque je génère le code pour appeler la procédure stockée, je ne veux pas prendre la peine d'ajouter le paramètre Address. Pour les paramètres INPUT, cela est correct car SQL Server me permet de fournir des valeurs par défaut. Mais pour le paramètre OUTPUT, que dois-je faire dans la procédure stockée pour le rendre facultatif, je ne reçois donc pas d'erreur ...

La procédure ou la fonction 'Person_InsertPerson' attend le paramètre '@AddressId', qui n'a pas été fourni.

69
Justin

Les paramètres d'entrée et de sortie peuvent être affectés par défaut. Dans cet exemple:

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

le premier paramètre est requis, et les deuxième et troisième sont facultatifs - s'ils ne sont pas définis par la routine d'appel, les valeurs par défaut leur seront attribuées. Essayez de jouer avec lui et la routine d'appel de test suivante dans SSMS en utilisant différentes valeurs et paramètres pour voir comment tout cela fonctionne ensemble.

DECLARE @Output int

SET @Output = 3

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

PRINT '---------'
PRINT @Output
107
Philip Kelley

Les paramètres de sortie et les valeurs par défaut ne fonctionnent pas bien ensemble! Cela vient de SQL 10.50.1617 (2008 R2). Ne vous laissez pas berner en croyant que cette construction fait magiquement un SET à cette valeur en votre nom (comme mon collègue l'a fait)!

Ce "jouet" SP interroge la valeur du paramètre OUTPUT, que ce soit la valeur par défaut ou 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

Si vous envoyez une valeur non initialisée (c'est-à-dire NULL) pour le OUTPUT, vous obtenez vraiment NULL à l'intérieur du SP, et non 0. Cela a du sens, quelque chose a été passé pour ce paramètre.

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

la sortie est:

wtf its NULL
@QR=NULL

Si nous ajoutons un SET explicite de l'appelant, nous obtenons:

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

et la sortie (sans surprise):

@QR=999

Encore une fois, cela a du sens, un paramètre est passé et SP n'a pris aucune action explicite pour SET une valeur.

Ajoutez un SET du paramètre OUTPUT dans le SP (comme vous êtes censé le faire), mais ne définissez rien à partir de l'appelant:

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

Maintenant, une fois exécuté:

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

la sortie est:

wtf its NULL
@QR=1234

Il s'agit du comportement "standard" pour la gestion des paramètres OUTPUT dans les SP.

Maintenant pour le torsion du tracé: La seule façon d'obtenir la valeur par défaut pour "activer", est de ne pas passer du tout le paramètre OUTPUT, qui à mon humble avis n'a pas de sens: étant donné qu'il est configuré en tant que paramètre OUTPUT, cela signifierait renvoyer quelque chose d '"important" qui devrait être collecté.

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

donne cette sortie:

yay its zero
@QR=NULL

Mais cela ne parvient pas à capturer la sortie des SP, probablement le but de cela SP pour commencer.

À mon humble avis, cette combinaison de fonctionnalités est une construction douteuse que je considérerais comme une odeur de code (ouf !!)

13
escape-llc

On dirait que je peux simplement ajouter une valeur par défaut au paramètre OUTPUT telle que:

@AddressId int = -1 Output

Semble être médiocre en termes de lisibilité puisque AddressId est strictement conçu comme une variable OUTPUT. Mais ça marche. Veuillez me faire savoir si vous avez une meilleure solution.

4
Justin

Étant donné que vous exécutez une procédure stockée et non une instruction SQL, vous devez définir le type de commande de votre commande SQL sur Procédure stockée:

cmd.CommandType = CommandType.StoredProcedure;

Tiré de ici .

De plus, une fois l'erreur supprimée, vous pouvez utiliser la fonction nvl() de SQL dans votre procédure pour spécifier ce que vous souhaitez afficher lorsqu'une valeur NULL est rencontrée.

Désolé de ne pas avoir répondu correctement à la question ... vous avez dû vous méprendre. Voici un exemple de nvl, qui, je pense, pourrait y remédier un peu mieux?

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

L'instruction SQL ci-dessus retournerait 'n/a' si le champ supplier_city Contenait une valeur nulle. Sinon, il retournerait la valeur supplier_city.

1
rownage

Pour ajouter à ce que Philip a dit:

J'avais une procédure stockée dans ma base de données de serveur SQL qui ressemblait à ceci:

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

Et je l'appelais depuis mon code .net comme suit:

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

J'obtenais une exception System.Data.SqlClient.SqlException: la procédure ou la fonction attend le paramètre "@current_phase", qui n'a pas été fourni.

J'utilise également cette fonction ailleurs dans mon programme et je passe un paramètre et je gère celui de sortie. Pour ne pas avoir à modifier l'appel en cours, je viens de changer la procédure stockée pour rendre le paramètre de sortie également facultatif.

Il ressemble donc maintenant à ceci:

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