kullanıcı tanımlı tablo türüyle sp_executesql doğru davranmıyor


11

Sorun : sp_executesql parametresi olarak kullanıcı tanımlı tablo türleriyle ilgili bilinen bir sorun var mı? Cevap - hayır, ben aptalım.

Komut dosyası oluştur

Bu komut dosyası, tablo, yordam ve kullanıcı tanımlı tablo türlerinin her birini oluşturur (yalnızca sınırlı SQL Server 2008+).

  • Yığın amacı, evet, verilerin prosedürü gerçekleştirdiğine dair bir denetim sağlamaktır. Hiçbir kısıtlama yoktur, verilerin eklenmesini engelleyecek hiçbir şey yoktur.

  • Yordam parametre olarak kullanıcı tanımlı bir tablo türünü alır. Proc'un yaptığı tüm tabloya eklemek.

  • Kullanıcı tanımlı tablo türü de bataklık basit, sadece tek bir sütun

Aşağıdakilere karşı çalıştım 11.0.1750.32 (X64) ve 10.0.4064.0 (X64)Evet, kutunun yamanabileceğini biliyorum, bunu kontrol etmiyorum.

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

Problem üretimi

Bu komut dosyası sorunu gösterir ve yürütülmesi beş saniye sürer. Kullanıcı tanımlı tablo türlerimin iki örneğini oluşturuyorum ve sonra bunları iki farklı şekilde geçirmeyi deniyorum sp_executesql. İlk çağırmadaki parametre eşlemesi, SQL Profiler'den ne aldığımı taklit eder. Daha sonra prosedürü sp_executesqlsarıcı olmadan çağırıyorum .

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

Sonuçlar : Aşağıda sonuçlarım var. Tablo başlangıçta boş. Ben şimdiki zaman yayar ve iki çağrı yapmak sp_executesql. Geçerli zamanı geçirmek ve yaymak için 5 saniye bekledikten sonra saklı yordamın kendisini çağırıp sonunda denetim masasını dökümü bekliyorum.

Zaman damgasından görebileceğiniz gibi, B kaydının kaydı düz saklı yordam çağrısına karşılık gelir. Ayrıca, SQLC kaydı yoktur.

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

Yırtma komut dosyası

Bu komut dosyası nesneleri doğru sırada kaldırır (yordamın başvurusu kaldırılmadan türü bırakamazsınız)

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

Bu neden önemli?

Kod aptalca görünüyor ama ben proc "düz" bir çağrı vs sp_execute kullanımı üzerinde çok kontrol yok. Çağrı zincirini daha yukarıda, ADO.NET kütüphanesini kullanıyorum ve daha önce yaptığım gibi bir TVP'ye geçiyorum . Parametrenin türü doğru System.Data.SqlDbType.Structured olarak ve CommandType da System.Data.CommandType.StoredProcedure olarak ayarlanır .

Neden bir çaylağım

Rob ve Martin Smith görmediğimi gördüler - sp_executesql'e iletilen ifade @MetricDataparametreyi kullanmadı . Geçilmemiş / eşlenmiş bir parametre kullanılmayacak.

PowerShell benim çalışma C # tablo değerli parametre kodunu tercüme edildi ve derlenmiş beri hata kodu olamazdı ; hariç. Bu soruyu yukarı yazarken, ben ayarlanmamış fark etti CommandTypeüzere StoredProcedurebu çizgiyi ekledi ve kodu yeniden koştum ama sorun değildi farz böylece profilcisine içinde iz değişmedi bu yüzden.

Komik hikaye - güncellenen dosyayı kaydetmezseniz , çalıştırmak bir fark yaratmaz. Bu sabah var ve (kaydettikten sonra) yeniden koştu ve ADO.NET EXEC dbo.Repro @MetricData=@p3hangi gayet iyi çalışır çevirdi .

Yanıtlar:


10

sp_executesqlad-hoc T-SQL yürütmek içindir. Bu yüzden denemelisiniz:

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

1
+1 Şu anda OP'deki komut dosyası sadece ifadeye sahiptir dbo.Reprove iletilen parametreleri hiç kullanmaz.
Martin Smith

Yesssss, Rob'un yorumunu gördüm ve orada EXEC kısmına ihtiyaç duymasa da, parametrenin çağrı ile kullanılmamasının nedeninin parametrenin kodda referans gösterilmemesi mantıklıydı.
Aptallığımı

@billinkc - Ah Daha ayrıntılı bir açıklama ile bir cevap ekledim ve yorumunuzu gördüm. Bu cevabı biraz sonra silerim.
Martin Smith

Sağ. Bunu ado.net'ten yaptığınızı söylememiştiniz. sp_executesql, .StoredProcedure'a değil, bir .CommandText öğesine eşdeğerdir.
Rob Farley

3

Bu soruyu silmeye cazip geldim, ancak başka birinin de benzer bir durumla karşılaşabileceğini düşündüm.

Temel neden benim $updateCommandCommandType ayarlamamıştı. Varsayılan değer Metin'dir, bu nedenle saklı yordamım doğru bir şekilde çağrıldı. Parametrelerim oluşturuluyor ve iletiliyordu, ancak diğer cevaplarda belirtildiği gibi, parametreler mevcut olduğu için kullanıldıkları anlamına gelmiyor .

Birinin benim hatamla ilgilenmesi durumunda kod

    $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 değeri ile çalışmak için @ rob_farley'nin değerini $updateQuery"EXEC dbo.Repro @MetricData" olarak değiştirme çözümü gerekir . Bu noktada, sp_executesql değerleri doğru şekilde eşler ve yaşam iyidir.

Bir ile Koşu CommandTypeait TableDirectbelgelere işaret olarak, SqlClient veri Sağlayıcısı tarafından desteklenmemektedir.

Bir Kullanarak CommandTypeait StoredProcedureolmadan saklı yordam doğrudan çağırma anlamına sp_executesqlsarıcı ve iyi çalışıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.