Question

Problème : est-il un problème connu avec des tables définies par l'utilisateur en tant que paramètres sp_executesql ? Réponse -. Non, je suis un idiot

Script Configuration

Ce script crée un chacun de la table, la procédure et le type de table défini par l'utilisateur (limité SQL Server 2008+ uniquement).

  • Le but du tas est de fournir une vérification que oui, les données ont dans la procédure. Il n'y a aucune contrainte, ne rien à éviter que des données soient insérées.

  • La procédure prend comme paramètre un type de table défini par l'utilisateur. Tout le proc est fait insérer dans la table.

  • Le type de table défini par l'utilisateur est simple bog aussi bien, juste une seule colonne

J'ai couru le suivant contre 11.0.1750.32 (X64) et 10.0.4064.0 (X64) Oui, je sais que la boîte pourrait être patché, je ne contrôle pas.

-- this table record that something happened
CREATE TABLE dbo.UDTT_holder
(
    ServerName varchar(200)
,   insert_time datetime default(current_timestamp)
)
GO

-- user defined table type transport mechanism
CREATE TYPE dbo.UDTT
AS TABLE
(
    ServerName varchar(200)
)
GO

-- stored procedure to reproduce issue
CREATE PROCEDURE dbo.Repro
(
    @MetricData dbo.UDTT READONLY
)
AS
BEGIN
    SET NOCOUNT ON

    INSERT INTO dbo.UDTT_holder 
    (ServerName)
    SELECT MD.* FROM @MetricData MD
END
GO

reproduction problème

Ce script illustre le problème et devrait prendre cinq secondes pour exécuter. Je crée deux instances de mes types de table définis par l'utilisateur, puis essayer deux moyens différents de les transmettre pour sp_executesql. Le mappage des paramètres dans les premiers imitateurs d'invocation de ce que je Capturé SQL Profiler. J'appelle ensuite la procédure sans l'emballage de sp_executesql.

SET NOCOUNT ON
DECLARE 
    @p3 dbo.UDTT
,   @MetricData dbo.UDTT
INSERT INTO @p3 VALUES(N'SQLB\SQLB')
INSERT INTO @MetricData VALUES(N'SQLC\SQLC')

-- nothing up my sleeve
SELECT * FROM dbo.UDTT_holder

SELECT CONVERT(varchar(24), current_timestamp, 121) + ' Firing sp_executesql' AS commentary
-- This does nothing
EXECUTE sp_executesql N'dbo.Repro',N'@MetricData dbo.UDTT READONLY',@MetricData=@p3
-- makes no matter if we're mapping variables
EXECUTE sp_executesql N'dbo.Repro',N'@MetricData dbo.UDTT READONLY',@MetricData

-- Five second delay
waitfor delay '00:00:05'
SELECT CONVERT(varchar(24), current_timestamp, 121) + ' Firing proc' AS commentary
-- this does
EXECUTE dbo.Repro @p3

-- Should only see the latter timestamp
SELECT * FROM dbo.UDTT_holder

GO

Résultats : ci-dessous sont mes résultats. Le tableau est initialement vide. J'émets l'heure actuelle et faire les deux appels à la sp_executesql. J'attends pendant 5 secondes pour passer et émettre heure puis en appelant la procédure stockée elle-même et enfin vider la table de vérification.

Comme vous pouvez le voir sur l'horodatage, le record du record correspond B à l'appel de la procédure stockée directement. En outre, il n'y a pas d'enregistrement SQLC.

ServerName                                       insert_time
------------------------------------------------------------------------

commentary
-------------------------------------------------
2012-02-02 13:09:05.973 Firing sp_executesql

commentary
-------------------------------------------------
2012-02-02 13:09:10.983 Firing proc

ServerName                                       insert_time
------------------------------------------------------------------------
SQLB\SQLB                                        2012-02-02 13:09:10.983

Abattez le script

Ce script supprime les objets dans l'ordre correct (vous ne pouvez pas supprimer le type avant a été supprimé la référence de la procédure)

-- cleanup
DROP TABLE dbo.UDTT_holder
DROP PROCEDURE dbo.Repro
DROP TYPE dbo.UDTT
GO

Pourquoi cette question est importante

Le code semble avoir idiot, mais je ne suis pas beaucoup de contrôle sur l'utilisation du sp_execute vs un appel « droit » au proc. Plus haut dans la chaîne d'appel, j'utilise la bibliothèque de ADO.NET et en passant dans un PVT comme I ont fait précédemment . Le type du paramètre est réglé correctement System.Data.SqlDbType.Structured et le CommandType est System.Data. CommandType.StoredProcedure .

Pourquoi je suis un noob (modifier) ??

Rob et Martin Smith a vu ce que je ne voyais pas - la déclaration étant transmis à sp_executesql ne pas utiliser le paramètre @MetricData. Un paramètre non passe jeu / cartographié ne va pas se habituer.

Je traduisais mon travail C # Code table paramètres d'une valeur de PowerShell et puisqu'il a compilé, il ne pouvais pas être le code fautif; sauf qu'il était. Tout en écrivant cette question, je me suis aperçu que je n'avais pas mis le CommandType à StoredProcedure alors j'ai ajouté cette ligne et re-couru le code, mais la trace dans profileur n'a pas changé, donc je suppose que la question n'a pas été.

histoire drôle - si vous ne le faites pas Enregistrer le fichier mis à jour, en cours d'exécution, il ne fera pas une différence. Je suis arrivé ce matin et re-couru (après avoir sauvé) et ADO.NET traduis à EXEC dbo.Repro @MetricData=@p3 qui fonctionne très bien.

Était-ce utile?

La solution

sp_executesql est pour l'exécution ad hoc T-SQL. Donc, vous devriez essayer:

EXECUTE sp_executesql N'exec dbo.Repro @MetricData',N'@MetricData dbo.UDTT READONLY',@MetricData=@p3

Autres conseils

J'ai été tenté de supprimer cette question, mais quelqu'un d'autre pourrait figurée courir dans une situation similaire.

La cause racine était mon $updateCommand avait pas mis la CommandType. La valeur par défaut est donc mon texte procédure stockée a été appelée correctement. Mes paramètres ont été créés et passés, mais comme indiqué dans les autres réponses juste parce que les paramètres sont disponibles ne signifie pas qu'ils sont utilisé .

code au cas où quelqu'un était intéressé par mon erreur

    $updateConnection = New-Object System.Data.SqlClient.SqlConnection("Data Source=localhost\localsqla;Initial Catalog=baseline;Integrated Security=SSPI;")
    $updateConnection.Open()

    $updateCommand = New-Object System.Data.SqlClient.SqlCommand($updateQuery)
    $updateCommand.Connection = $updateConnection

    # This is what I was missing
    $updateCommand.CommandType = [System.Data.CommandType]::StoredProcedure
    #$updateCommand.CommandType = [System.Data.CommandType]::Text
    #$updateCommand.CommandType = [System.Data.CommandType]::TableDirect

    $DataTransferFormat = $updateCommand.Parameters.AddWithValue("@MetricData", $dataTable)
    $DataTransferFormat.SqlDbType = [System.Data.SqlDbType]::Structured
    $DataTransferFormat.TypeName = $udtt
    $results = $updateCommand.ExecuteNonQuery()

L'exécution d'une valeur de CommandType du texte nécessiterait @ la solution de rob_farley de changer la valeur de $updateQuery à « exec dbo.Repro @MetricData ». À ce moment-là, le sp_executesql cartographierons correctement les valeurs et la vie est bonne.

Courir avec un CommandType de TableDirect est pas pris en charge par le fournisseur de données SqlClient, que la documentation indique.

L'utilisation d'un CommandType de StoredProcedure se traduit par une invocation directe de la procédure stockée sans l'emballage de sp_executesql et fonctionne très bien.

Licencié sous: CC-BY-SA avec attribution
Non affilié à dba.stackexchange
scroll top