Domanda

Problema : esiste un problema noto con definiti dall'utente tipi di tabella come parametri per sp_executesql ? Risposta -. No, io sono un idiota

Impostare lo script

Questo script crea uno ciascuno di tabella, la procedura e tipo di tabella definito dall'utente (limitato SQL Server 2008+ solo).

  • Lo scopo del mucchio è quello di fornire una verifica che sì, i dati messi in procedura. Non ci sono vincoli, niente di niente per evitare che i dati vengano inseriti.

  • La procedura richiede come parametro un definito dall'utente tipo di tabella. Tutto il proc fa è inserimento nella tabella.

  • Il tipo di tabella definito dall'utente è semplice palude così, solo una singola colonna

Ho eseguito il seguente contro 11.0.1750.32 (X64) e 10.0.4064.0 (X64) Sì, lo so che la casella potrebbe essere corretto, non mi controllo che.

-- 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

riproduzione Problema

Questo script viene illustrato il problema e dovrebbe prendere cinque secondi per eseguire. Creo due istanze dei miei tipi di tabella definiti dall'utente e quindi provare due diversi mezzi di passarli a sp_executesql. La mappatura dei parametri nei primi imita invocazione quello che ho catturato da SQL Profiler. Ho poi chiamo la procedura senza l'involucro 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

Risultati : di seguito sono i miei risultati. La tabella è inizialmente vuota. Emetto ora corrente ed effettuare le due chiamate al sp_executesql. Aspetto per 5 secondi per passare e emettere ora corrente seguita chiamando la stored procedure in sé e, infine, eseguire il dump della tabella di controllo.

Come si può vedere dalla data e ora, il record per le corrisponde discografiche B alla procedura di richiamo dritto memorizzato. Inoltre, non v'è alcuna traccia 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

abbattere lo script

Questo script rimuove gli oggetti nell'ordine corretto (non è possibile eliminare il tipo prima di riferimento del procedimento è stata rimossa)

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

Perché questo è importante

Il codice sembra stupido, ma non ho molto controllo su l'uso del sp_execute vs una chiamata "dritto" al proc. Più in alto la catena di chiamate, io sto usando la biblioteca ADO.NET e passando in una TVP, come ho hanno fatto in precedenza . Il tipo di parametro è impostato correttamente per System.Data.SqlDbType.Structured e il CommandType è impostato su System.Data. CommandType.StoredProcedure .

Perché io sono un noob (edit)

Rob e Martin Smith ha visto quello che non stavo vedendo - la dichiarazione di essere passati alla sp_executesql non utilizzare il parametro @MetricData. Un parametro unpassed / mappata non ha intenzione di abituarsi.

stavo traducendo il mio codice-parametro con valori di tabella C # lavorare per PowerShell e dal momento che compilato, riuscivo a non essere il codice in colpa; tranne che era. Mentre scrivevo questa domanda, mi sono reso conto che non avevo impostato il CommandType a StoredProcedure così ho aggiunto che la linea e ri-fatto funzionare il codice, ma la traccia di profiler non ho cambiato così ho pensato che non era il problema.

Storia divertente - se non lo fai Salva il file aggiornato, in esecuzione non farà la differenza. Ho avuto in questa mattina e ri-fatto funzionare (dopo il salvataggio) e ADO.NET tradotto a EXEC dbo.Repro @MetricData=@p3 che funziona bene.

È stato utile?

Soluzione

sp_executesql è per l'esecuzione ad hoc T-SQL. Così si dovrebbe provare:

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

Altri suggerimenti

Sono stato tentato di eliminare questa domanda, ma qualcun altro potrebbe figurato incorrere in una circostanza simile.

La causa principale è stata la mia $updateCommand non aveva fissato il CommandType. Il valore predefinito è Testo quindi il mio stored procedure era stato chiamato in modo corretto. I miei parametri sono stati creati e passato, ma come notato nelle altre risposte solo perché i parametri sono disponibili non significa che stanno essendo utilizzato .

Codice nel caso qualcuno era interessato a mio errore

    $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()

Correndo con un valore CommandType di testo richiederebbe @ soluzione rob_farley di cambiare il valore di $updateQuery a "EXEC dbo.Repro @MetricData". A quel punto, lo sp_executesql sarà mappa i valori in modo corretto e la vita è buona.

Correndo con un CommandType di TableDirect non è supportata dal provider SqlClient di dati, come indica la documentazione.

Utilizzando un CommandType di StoredProcedure si traduce in un'invocazione diretta della stored procedure, senza l'involucro sp_executesql e funziona alla grande.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top