Hatalar: "INSERT EXEC ifadesi yuvalanamaz." ve "INSERT-EXEC deyimi içinde ROLLBACK ifadesi kullanılamaz." Bunu nasıl çözebilirim?


100

Üç saklı prosedürlere sahip Sp1, Sp2ve Sp3.

İlki ( Sp1), ikincisini ( ) çalıştıracak Sp2ve döndürülen verileri içine kaydedecek @tempTB1ve ikincisi, üçüncü olanı ( Sp3) çalıştıracak ve verileri içine kaydedecektir @tempTB2.

Çalıştırırsam Sp2işe yarayacak ve bana tüm verilerimi geri getirecek Sp3, ancak sorun içinde, Sp1onu çalıştırdığımda şu hatayı gösterecek:

INSERT EXEC ifadesi yuvalanamaz

Yerini değiştirmeye çalıştım execute Sp2ve bana başka bir hata gösteriyor:

INSERT-EXEC deyimi içinde ROLLBACK deyimi kullanılamaz.

Yanıtlar:


102

Bu, depolanan yordamlar zincirindeki verileri "kabarcıklara ayırmaya" çalışırken karşılaşılan yaygın bir sorundur. SQL Server'daki bir kısıtlama, aynı anda yalnızca bir INSERT-EXEC'in etkin olabilmesidir. Bu tür bir sorunu çözmek için modeller hakkında çok kapsamlı bir makale olan Depolanan Yordamlar Arasında Veri Paylaşımı'na bakmanızı öneririm .

Örneğin, geçici bir çözüm, Sp3'ü Tablo değerli bir işleve dönüştürmek olabilir.


1
bozuk bağlantı VEYA yanıt vermeyen site.
SouravA

6
Buna izin vermemenin teknik sebebinin ne olduğu hakkında bir fikriniz var mı? Bununla ilgili herhangi bir bilgi bulamıyorum.
jtate

1
Ne yazık ki bu genellikle bir seçenek değildir. Birçok önemli bilgi türü yalnızca sistemde depolanan yordamlardan güvenilir bir şekilde elde edilebilir (çünkü belirli durumlarda ilgili yönetim görünümü güvenilmez / eski veriler içerir; bir örnek, tarafından döndürülen bilgilerdir ). sp_help_jobactivity
GSerg

21

Bu, her ikisi de korkunç çözümler olan dev bir kıvrımlı oluşturulmuş işlev veya yürütülen sql string çağrısı olmadan SQL Server'da bunu yapmanın tek "basit" yoludur:

  1. geçici bir tablo oluştur
  2. openrowsetkinleştirilmiş yordam verilerinizi içine ayarlayın

MİSAL:

INSERT INTO #YOUR_TEMP_TABLE
SELECT * FROM OPENROWSET ('SQLOLEDB','Server=(local);TRUSTED_CONNECTION=YES;','set fmtonly off EXEC [ServerName].dbo.[StoredProcedureName] 1,2,3')

Not : 'set fmtonly off' kullanmanız ZORUNLUDUR VE buna, saklı yordam parametrelerinizi içeren dizge veya tablo adı için openrowset çağrısının içinde dinamik sql ekleyemezsiniz. Bu nedenle, çoğu durumda geçici tablo gerçekleştirdiği için tablo değişkenleri yerine geçici bir tablo kullanmanız gerekir, ki bu daha iyi olurdu.


SET FMTONLY OFF'u kullanmak zorunlu değildir. Prosedürün normalde döndürdüğü veri türleriyle boş bir tablo döndüren bir IF (1 = 0) ekleyebilirsiniz.
Guillermo Gutiérrez

2
Şablon Tablolar ve Tablo değişkenleri verilerini farklı şekilde depolar. Sorgu iyileştirici tablo değişkenleri üzerindeki istatistikleri tutmadığından, tablo değişkenlerinin küçük sonuç kümeleri için kullanılması gerekir. Bu nedenle, büyük veri kümeleri için Temp tablolarını kullanmak neredeyse her zaman daha iyidir. İşte bununla ilgili güzel bir blog makalesi mssqltips.com/sqlservertip/2825/…
gh9

@ gh9 evet, ancak bu yine de büyük sonuç kümeleri için korkunç bir fikir. Geçici veritabanında gerçek bir tablonun istatistikleri ve kullanımı önemli ek yüklere neden olabilir. 1 satır geçerli değer içeren bir kayıt kümesini döndüren (birkaç tabloyu sorgulayan) ve bunu bir tablo değişkeninde depolayan ve aynı biçime sahip başka bir tablodaki değerlerle karşılaştıran bir yordamım var. Geçici tablodan bir tablo değişkenine geçiş, ortalama süreyi 8 ms'den 2 ms'ye çıkarmıştır; bu, gün boyunca saniyede birkaç kez ve gece işleminde 100.000 kez çağrıldığında önemlidir.
Jason Goemaat

1
İstatistiklerin bir tablo değişkeninde tutulmasını neden isteyesiniz? Bütün mesele, RAM'de sorgu bittikten sonra yok edilecek geçici bir tablo oluşturmaktır. Tanım gereği, böyle bir tabloda oluşturulan herhangi bir istatistik asla kullanılmayacaktır. Genel olarak, bir tablo değişkenindeki verilerin mümkün olan her yerde RAM'de kalması gerçeği, verilerinizin SQL Server için mevcut olan RAM miktarından daha küçük olduğu herhangi bir senaryoda onları Şablon Tablolarından daha hızlı kılar (bu günlerde SQL sunucumuz için 100GB + bellek havuzları) Sunucular, neredeyse her zaman)
Geoff Griswald

Bu, genişletilmiş saklı yordamlar için çalışmaz. Hata şudur : Metadata belirlenemedi çünkü 'Yordamdaki' EXECUTE <yordamadı> @retval OUTPUT 'ifadesi genişletilmiş bir saklı yordamı çağırıyor .
GSerg

12

Tamam, jimhark tarafından teşvik edilen eski tek karma tablo yaklaşımına bir örnek: -

CREATE PROCEDURE SP3 as

BEGIN

    SELECT 1, 'Data1'
    UNION ALL
    SELECT 2, 'Data2'

END
go


CREATE PROCEDURE SP2 as

BEGIN

    if exists (select  * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
        INSERT INTO #tmp1
        EXEC SP3
    else
        EXEC SP3

END
go

CREATE PROCEDURE SP1 as

BEGIN

    EXEC SP2

END
GO


/*
--I want some data back from SP3

-- Just run the SP1

EXEC SP1
*/


/*
--I want some data back from SP3 into a table to do something useful
--Try run this - get an error - can't nest Execs

if exists (select  * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
    DROP TABLE #tmp1

CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))

INSERT INTO #tmp1
EXEC SP1


*/

/*
--I want some data back from SP3 into a table to do something useful
--However, if we run this single hash temp table it is in scope anyway so
--no need for the exec insert

if exists (select  * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
    DROP TABLE #tmp1

CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))

EXEC SP1

SELECT * FROM #tmp1

*/

Bu geçici çözümü de kullandım. Fikir için teşekkürler!
SQL_Guy

Harika bir çalışma ortamı. Bu, geçici tablo kapsamı hakkında daha fazla şey öğrenmeme yardımcı oldu. Örneğin, dışında bildirilmişse bir dynsql dizesinde geçici bir tablo kullanabileceğinizi bilmiyordum. Burada benzer konsept. Çok teşekkürler.
jbd

10

Bu soruna yönelik çalışmam her zaman, tek karma geçici tabloların herhangi bir procs kapsamına girdiği ilkesini kullanmak olmuştur. Bu yüzden, proc parametrelerinde bir seçenek anahtarım var (varsayılan ayar kapalı). Bu açıksa, çağrılan proc sonuçları çağıran proc'ta oluşturulan geçici tabloya ekleyecektir. Sanırım geçmişte bunu bir adım daha ileri götürdüm ve çağrılan proc'a bir kod koyup tek bir hash tablonun kapsam dahilinde olup olmadığını kontrol ettim, eğer varsa o zaman kodu ekledim, aksi takdirde sonuç kümesini döndürdüm. İyi çalışıyor gibi görünüyor - büyük veri kümelerini işlemler arasında geçirmenin en iyi yolu.


1
Bu cevabı beğendim ve bahse girerim eğer örnek verirseniz daha fazla oy alırsınız.
jimhark

Bunu yıllardır yapıyorum. Yine de SQL Azure'da gerekli mi?
Nick Allan

evet, Azure, SQL Server ile tamamen aynı motoru kullanıyor, tek gerçek fark depolama ve CPU
Geoff Griswald

6

Bu numara benim için çalışıyor.

Uzak sunucuda bu sorunu yaşamazsınız çünkü uzak sunucuda, son ekleme komutu bir önceki komutun sonucunun yürütülmesini bekler. Aynı sunucuda durum böyle değil.

Bir geçici çözüm için bu durumdan yararlanın.

Bağlantılı Sunucu oluşturmak için doğru izniniz varsa, yapın. Bağlı sunucu ile aynı sunucuyu oluşturun.

  • SSMS'de sunucunuza giriş yapın
  • "Sunucu Nesnesine gidin
  • "Bağlı Sunucular" ı ve ardından "Yeni Bağlı Sunucu" yu sağ tıklayın
  • iletişim kutusunda, bağlı sunucunuzun herhangi bir adını verin: örneğin: THISSERVER
  • sunucu türü "Diğer veri kaynağı" dır
  • Sağlayıcı: SQL sunucusu için Microsoft OLE DB Sağlayıcısı
  • Veri kaynağı: IP'niz, aynı zamanda sadece bir nokta (.) Da olabilir, çünkü localhost
  • "Güvenlik" sekmesine gidin ve üçüncüsü "Oturum açmanın mevcut güvenlik içeriği kullanılarak yapılsın" ı seçin
  • İsterseniz sunucu seçeneklerini (3. sekme) düzenleyebilirsiniz.
  • Tamam'a basın, bağlantılı sunucunuz oluşturulur

şimdi SP1'deki Sql komutunuz

insert into @myTempTable
exec THISSERVER.MY_DATABASE_NAME.MY_SCHEMA.SP2

İnanın bana, SP2'de dinamik ekiniz olsa bile çalışıyor


4

Bir çözüm buldum, prodlardan birini tablo değerli bir işleve dönüştürmek. Bunun her zaman mümkün olmadığının farkındayım ve kendi sınırlarını ortaya koyuyor. Ancak, prosedürlerden en az birini her zaman bunun için iyi bir aday bulabildim. Bu çözümü seviyorum çünkü çözüme herhangi bir "hack" getirmiyor.


ancak bir dezavantaj, işlev karmaşıksa istisna işlemeyle ilgili bir sorundur, değil mi?
Muflix

3

Depolanan Proc sonuçlarını geçici bir tabloya aktarmaya çalışırken ve Depolanan Proc kendi işleminin bir parçası olarak geçici bir tabloya eklerken bu sorunla karşılaştım. Sorun, SQL Server'ın aynı işlemin aynı anda iki farklı geçici tabloya yazmasına izin vermemesidir.

Kabul edilen OPENROWSET cevabı iyi çalışıyor, ancak sürecimde herhangi bir Dinamik SQL veya harici bir OLE sağlayıcısı kullanmaktan kaçınmam gerekti, bu yüzden farklı bir yola gittim.

Bulduğum kolay bir çözüm, saklı yordamımdaki geçici tabloyu bir tablo değişkenine değiştirmekti. Geçici bir tabloyla olduğu gibi tam olarak aynı şekilde çalışır, ancak artık diğer geçici tablo eklentimle çakışmaz.

Sadece ... performans katiller olarak Tablo Değişkenler Beni uyarı, senden birkaç yazma üzere olduğunu biliyoruz yorumunu atlatma amaçlı Eğer 2020 yılında bu temettü ödeyen olmasıdır Tüm söyleyebileceğim değil Tablo Değişkenler korkmak. Bu 2008 ise ve Veritabanım 16GB RAM'e sahip bir sunucuda barındırıldıysa ve 5400 RPM HDD'ler tükeniyorsa, sizinle aynı fikirde olabilirim. Ancak 2020 ve birincil depolama alanım olarak bir SSD dizim ve yüzlerce konser RAM'im var. Tüm şirketimin veritabanını bir tablo değişkenine yükleyebilir ve yine de yedeklenecek bol miktarda RAM'im olabilir.

Tablo Değişkenleri menüye geri döndü!


1

Aynı sorunu ve endişeyi iki veya daha fazla zincir dişlisinde yinelenen kodla yaşadım. Sonunda "mod" için ek bir özellik ekledim. Bu, ortak kodun bir sproc ve mod yönlendirmeli akış ve sproc'un sonuç kümesi içinde var olmasına izin verdi.


1

çıktıyı statik tabloya kaydetmeye ne dersiniz? Sevmek

-- SubProcedure: subProcedureName
---------------------------------
-- Save the value
DELETE lastValue_subProcedureName
INSERT INTO lastValue_subProcedureName (Value)
SELECT @Value
-- Return the value
SELECT @Value

-- Procedure
--------------------------------------------
-- get last value of subProcedureName
SELECT Value FROM lastValue_subProcedureName

ideal değil, ama çok basit ve her şeyi yeniden yazmanıza gerek yok.

GÜNCELLEME : önceki çözüm paralel sorgularla (zaman uyumsuz ve çok kullanıcılı erişim) iyi çalışmıyor, bu nedenle şimdi geçici tablolar kullanıyorum

-- A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished. 
-- The table can be referenced by any nested stored procedures executed by the stored procedure that created the table. 
-- The table cannot be referenced by the process that called the stored procedure that created the table.
IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NULL
CREATE TABLE #lastValue_spGetData (Value INT)

-- trigger stored procedure with special silent parameter
EXEC dbo.spGetData 1 --silent mode parameter

yuvalanmış spGetDatasaklı yordam içeriği

-- Save the output if temporary table exists.
IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NOT NULL
BEGIN
    DELETE #lastValue_spGetData
    INSERT INTO #lastValue_spGetData(Value)
    SELECT Col1 FROM dbo.Table1
END

 -- stored procedure return
 IF @silentMode = 0
 SELECT Col1 FROM dbo.Table1

Genelde, Tablolarda olduğu gibi geçici bir SProc oluşturamazsınız. Bu yaklaşım henüz tam olarak bilinmediğinden veya kabul edilmediğinden, örneğinizi daha fazla referansla genişletmeniz gerekecektir. Ayrıca, ANSI-SQL'in Lambda İfadesi yaklaşımlarına izin vermediği bir Lambda İfadesine SProc uygulamasından daha çok benzer.
GoldBishop

Çalışıyor, ancak paralel sorgularla (zaman uyumsuz ve çoklu kullanıcı erişimleri) iyi çalışmadığını buldum. Bu nedenle şimdi geçici tablo yaklaşımını kullanıyorum. Cevabımı güncelledim.
Muflix

1
Temp tablosu mantığı iyi, ilgilendiğim SProc referansıydı. Sproc's doğası gereği doğrudan sorgulanamaz. Tablo Değerli Fonksiyonlar doğrudan sorgulanabilir. Güncellenmiş mantığınızda bahsettiğiniz gibi olmalı, en iyi yaklaşım bir Geçici Tablo, oturum, örnek veya globaldir ve bu noktadan hareket eder.
GoldBishop

0

İç sp'ye bir çıktı imleci değişkeni bildirin:

@c CURSOR VARYING OUTPUT

Sonra geri dönmek istediğiniz seçime bir imleç bildiriniz. Ardından imleci açın. Ardından referansı ayarlayın:

DECLARE c CURSOR LOCAL FAST_FORWARD READ_ONLY FOR 
SELECT ...
OPEN c
SET @c = c 

KAPATMAYIN veya yeniden tahsis ETMEYİN.

Şimdi, aşağıdaki gibi bir imleç parametresi sağlayan dış olandan iç sp'yi çağırın:

exec sp_abc a,b,c,, @cOUT OUTPUT

İç sp çalıştırıldığında, @cOUTalmaya hazırsınız. Döngüleyin ve ardından kapatın ve ayırmayı kaldırın.


0

C # gibi diğer ilişkili teknolojileri kullanabiliyorsanız, yerleşik SQL komutunu Transaction parametresiyle kullanmanızı öneririm.

var sqlCommand = new SqlCommand(commandText, null, transaction);

Bu yeteneği gösteren ve burada bulabileceğiniz basit bir Konsol Uygulaması oluşturdum: https://github.com/hecked12/SQL-Transaction-Using-C-Sharp

Kısacası, C #, her bir saklı yordamın çıktısını inceleyebileceğiniz ve bu çıktıyı istediğiniz gibi kullanabileceğiniz, örneğin başka bir saklı yordama besleyebileceğiniz bu sınırlamanın üstesinden gelmenizi sağlar. Çıktı tamamsa, işlemi gerçekleştirebilirsiniz, aksi takdirde değişiklikleri geri alma özelliğini kullanarak geri döndürebilirsiniz.


-1

SQL Server 2008 R2'de, Geri Alma hatasına neden olan tablo sütunlarında bir uyuşmazlık vardı. İnsert-exec deyimi tarafından doldurulan sqlcmd tablo değişkenimi, depolanan proc tarafından döndürülenle eşleşecek şekilde düzelttiğimde kayboldu. Org_code eksikti. Windows cmd dosyasında, saklı yordamın sonucunu yükler ve seçer.

set SQLTXT= declare @resets as table (org_id nvarchar(9), org_code char(4), ^
tin(char9), old_strt_dt char(10), strt_dt char(10)); ^
insert @resets exec rsp_reset; ^
select * from @resets;

sqlcmd -U user -P pass -d database -S server -Q "%SQLTXT%" -o "OrgReport.txt"

OP, yuvalanmış saklı yordamlarda insert-exec deyimleri kullanıldığında oluşan bir hatayı soruyordu. Sorununuz, "INSERT deyimi için seçim listesi, ekleme listesinden daha az öğe içeriyor. SELECT değerlerinin sayısı INSERT sütunlarının sayısıyla eşleşmelidir." Gibi farklı bir hata döndürür.
Losbear

Bu, bu mesajı yanlışlıkla almanın mümkün olduğuna dair daha çok bir uyarıdır.
user3448451
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.