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.
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
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 !!)
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.
É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
.
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)