사용자 정의 테이블 형식의 sp_executesql이 올바르게 작동하지 않습니다


11

문제점 : sp_executesql에 대한 매개 변수로 사용자 정의 테이블 유형에 알려진 문제점이 있습니까? 답변-아니요, 저는 바보입니다.

스크립트 설정

이 스크립트는 각 테이블, 프로 시저 및 사용자 정의 테이블 형식 중 하나를 만듭니다 (제한된 SQL Server 2008+ 만 해당).

  • 힙의 목적은 예, 데이터가이를 프로 시저로 만들었다는 감사를 제공하는 것입니다. 데이터가 삽입되는 것을 막을 수있는 제약은 없습니다.

  • 프로시 저는 사용자 정의 테이블 유형을 매개 변수로 사용합니다. 모든 프로세스는 테이블에 삽입됩니다.

  • 사용자 정의 테이블 유형은 단순하지만 단 하나의 열입니다.

나는에 대해 다음을 실행 한 11.0.1750.32 (X64) 및 10.0.4064.0 (X64)예, 나는 그것을 제어하지 않는, 그 상자를 패치 할 수 있습니다 알고있다.

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

문제 재현

이 스크립트는 문제를 보여 주며 실행하는 데 5 초가 걸립니다. 사용자 정의 테이블 형식의 두 인스턴스를 만든 다음에 전달하는 두 가지 다른 방법을 시도합니다 sp_executesql. 첫 번째 호출의 매개 변수 매핑은 SQL 프로파일 러에서 캡처 한 내용을 모방합니다. 그런 다음 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

결과 : 아래는 내 결과입니다. 테이블은 처음에 비어 있습니다. 현재 시간을 내고에 두 번 전화를 sp_executesql겁니다. 5 초 동안 기다렸다가 현재 시간을 내 보낸 다음 저장 프로 시저 자체를 호출하고 마지막으로 감사 테이블을 덤프합니다.

타임 스탬프에서 볼 수 있듯이 B 레코드의 레코드는 스트레이트 스토어드 프로 시저 호출에 해당합니다. 또한 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

스크립트를 찢어

이 스크립트는 객체를 올바른 순서로 제거합니다 (프로 시저 참조가 제거되기 전에 유형을 삭제할 수 없음)

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

이것이 중요한 이유

코드는 어리석은 것처럼 보이지만 proc에 대한 "직선적 인"호출에 대한 sp_execute의 사용을 많이 제어 할 수는 없습니다. 콜 체인을 높이면 ADO.NET 라이브러리를 사용하고 이전 과 같이 TVP를 전달 합니다. 매개 변수 유형은 System.Data.SqlDbType.Structured 로 올바르게 설정되고 CommandType은 System.Data.CommandType.StoredProcedure 로 설정됩니다 .

내가 멍청한 이유

Rob과 Martin Smith는 내가 보지 못한 것을 보았습니다. sp_executesql에 전달 된 명령문은 @MetricData매개 변수를 사용하지 않았습니다 . 전달되지 않은 / 매핑 된 매개 변수는 사용되지 않습니다.

작업중인 C # 테이블 반환 매개 변수 코드를 PowerShell로 변환하고 컴파일 된 이후 오류가 발생한 코드 일 수 없습니다 . 제외했다. 이 질문을 쓰는 동안, 나는 내가 설정하지 않았 실현 CommandType에 StoredProcedure난 그 라인을 추가하고 코드를 재 - 실행하지만이 문제가되지 않았 음을 가정 있도록 프로파일 러의 추적이 변경하지 않은 때문에.

재미있는 이야기- 업데이트 된 파일을 저장 하지 않으면 실행해도 차이가 없습니다. 오늘 아침에 다시 저장 한 후 (저장 후) ADO.NET에서 제대로 EXEC dbo.Repro @MetricData=@p3작동 하는 것으로 변환했습니다 .

답변:


10

sp_executesql임시 T-SQL을 실행하기위한 것입니다. 따라서 시도해야합니다 :

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

1
+1 현재 OP의 스크립트에는 명령문 만 있으며 dbo.Repro전달 된 매개 변수를 전혀 사용하지 않습니다.
— Martin Smith

Yesssss, Rob의 의견을 보았고 거기에 EXEC 부분이 필요하지 않지만 매개 변수가 호출에 사용되지 않은 이유는 매개 변수가 스크립트에서 참조되지 않았기 때문입니다. 어리 석음을 반영하여 티켓 업데이트
— billinkc

@billinkc-아 방금 자세한 설명과 함께 답변을 추가 한 다음 귀하의 의견을 보았습니다. 그 대답을 조금 후에 삭제하겠습니다.
— 마틴 스미스

권리. 당신은 당신이 ado.net에서 이것을하고 있다고 언급하지 않았습니다. sp_executesql은 .StoredProcedure가 아닌 .CommandText와 같습니다.
— Rob Farley 2012

3

나는이 질문을 삭제하려고 유혹했지만 다른 사람이 비슷한 상황에 빠질 수 있다고 생각했습니다.

근본 원인은 $updateCommandCommandType을 설정하지 않았기 때문입니다. 기본값은 Text이므로 저장 프로 시저가 올바르게 호출되었습니다. 내 매개 변수가 만들어지고 전달되었지만 다른 대답에서 언급했듯이 매개 변수를 사용할 수 있다고 해서 매개 변수가 사용되고 있다는 의미는 아닙니다 .

누군가 내 실수에 관심이있는 경우 코드

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

CommandType of Text 값으로 실행하려면 값 $updateQuery을 "EXEC dbo.Repro @MetricData" 로 변경하는 @rob_farley의 솔루션이 필요합니다 . 이 시점에서 sp_executesql은 값을 올바르게 매핑하고 수명은 좋습니다.

설명서에서 알 수 있듯이 SqlClient 데이터 공급자 는 CommandTypeof를 사용하여 실행할 수 TableDirect없습니다.

CommandTypeof를 사용 StoredProcedure하면 sp_executesql래퍼 없이 저장 프로 시저를 직접 호출 할 수 있으며 훌륭하게 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.