사용자 정의 테이블 형식의 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로 변환하고 컴파일 된 이후 오류가 발생한 코드 일 수 없습니다 . 제외했다. 이 질문을 쓰는 동안, 나는 내가 설정하지 않았 실현 CommandTypeStoredProcedure난 그 라인을 추가하고 코드를 재 - 실행하지만이 문제가되지 않았 음을 가정 있도록 프로파일 러의 추적이 변경하지 않은 때문에.

재미있는 이야기- 업데이트 된 파일을 저장 하지 않으면 실행해도 차이가 없습니다. 오늘 아침에 다시 저장 한 후 (저장 후) 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.