त्रुटियां: "INSERT EXEC कथन को नेस्टेड नहीं किया जा सकता है।" और "INSERT-EXEC स्टेटमेंट के भीतर ROLLBACK स्टेटमेंट का उपयोग नहीं कर सकता।" इसे कैसे हल करें?


98

मेरे पास तीन संग्रहीत कार्यविधियाँ हैं Sp1, Sp2और Sp3

पहला एक ( Sp1) दूसरे को निष्पादित करेगा ( Sp2और लौटाए गए डेटा को बचाएगा @tempTB1और दूसरा एक तीसरे को निष्पादित करेगा Sp3) और में डेटा को बचाएगा @tempTB2

अगर मैं Sp2इसे निष्पादित करता हूं तो यह काम करेगा और यह मुझे अपना सारा डेटा वापस कर देगा Sp3, लेकिन समस्या Sp1तब है, जब मैं इसे निष्पादित करता हूं तो यह त्रुटि प्रदर्शित करेगा:

INSERT EXEC कथन को नेस्टेड नहीं किया जा सकता है

मैंने इसके स्थान को बदलने की कोशिश की execute Sp2और इसने मुझे एक और त्रुटि दिखाई:

INSERT-EXEC स्टेटमेंट के भीतर ROLLBACK स्टेटमेंट का उपयोग नहीं कर सकते।

जवाबों:


102

संग्रहीत प्रक्रियाओं की एक श्रृंखला से डेटा को 'बुलबुला' करने का प्रयास करते समय यह एक सामान्य मुद्दा है। SQL सर्वर में प्रतिबंध केवल एक समय में एक INSERT-EXEC सक्रिय हो सकता है। मैं यह देखने की सलाह देता हूं कि स्टोर्ड प्रोसीजर के बीच डेटा को कैसे साझा किया जाए जो इस प्रकार की समस्या के आसपास काम करने के लिए पैटर्न पर बहुत गहन लेख है।

उदाहरण के लिए चारों ओर एक काम Sp3 को टेबल-वैल्यू फ़ंक्शन में बदल सकता है।


1
टूटी हुई लिंक या अनुत्तरदायी साइट।
सौरव

6
क्या आपको इस बात की कोई जानकारी है कि इसकी अनुमति न देने का तकनीकी कारण क्या है? मुझे इस पर कोई जानकारी नहीं मिली।
1919

1
दुर्भाग्य से यह अक्सर एक विकल्प नहीं होता है। कई प्रकार की महत्वपूर्ण जानकारी केवल सिस्टम स्टोरेज प्रक्रियाओं से विश्वसनीय रूप से उपलब्ध है (क्योंकि कुछ मामलों में संबंधित प्रबंधन के दृश्य में अप्रतिबंधित / अप्रचलित डेटा होते हैं; एक उदाहरण द्वारा दी गई जानकारी है )। sp_help_jobactivity
GSerg

21

SQL सर्वर में ऐसा करने का यह एकमात्र "सरल" तरीका है, जिसमें कुछ विशाल विक्षेपित निर्मित फंक्शन या निष्पादित एसक्यूएल स्ट्रिंग कॉल के बिना, दोनों ही भयानक समाधान हैं:

  1. एक अस्थायी तालिका बनाएं
  2. अपने संग्रहीत कार्यविधि डेटा को इसमें ओपन करता है

उदाहरण:

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

नोट : आप 'सेट fmtonly बंद' का उपयोग करना चाहिए, और आप या तो ओपनरोसेट कॉल के अंदर डायनेमिक sql जोड़ सकते हैं, या तो स्ट्रिंग के लिए अपने संग्रहीत कार्यविधि पैरामीटर या तालिका नाम के लिए। यही कारण है कि आपको टेबल वेरिएबल के बजाय टेम्प टेबल का उपयोग करना पड़ता है, जो बेहतर होता, क्योंकि यह ज्यादातर मामलों में टेम्प टेबल का प्रदर्शन करता है।


SET FMTONLY OFF का उपयोग करना आवश्यक नहीं है। आप केवल एक IF (1 = 0) जोड़ सकते हैं जो समान डेटा प्रकारों के साथ एक खाली तालिका लौटाता है जो प्रक्रिया सामान्य रूप से वापस आती है।
गुइलेर्मो गुतिरेज़

1
टेम्बल टेबल्स और टेबल वेरिएबल्स अपने डेटा को अलग तरह से स्टोर करते हैं। टेबल वेरिएबल्स का उपयोग छोटे परिणाम सेट के लिए किया जाना चाहिए क्योंकि क्वेरी ऑप्टिमाइज़र टेबल वेरिएबल्स पर आंकड़े बनाए नहीं रखता है। इसलिए बड़े डेटा सेट के लिए टेंप टेबल का इस्तेमाल करना लगभग हमेशा बेहतर होता है। यहाँ पर एक अच्छा ब्लॉग लेख है mssqltips.com/sqlservertip/2825/…
gh9

@ gh9 हाँ, लेकिन यह वैसे भी बड़े परिणाम सेट के लिए एक भयानक विचार है। अस्थायी डेटाबेस में एक वास्तविक तालिका के आँकड़े और उपयोग महत्वपूर्ण ओवरहेड का कारण बन सकते हैं। मेरे पास एक ऐसी प्रक्रिया है जो वर्तमान मानों की 1 पंक्ति (कई तालिकाओं को क्वेरी करते हुए) के साथ एक रिकॉर्डसेट लौटाती है और एक प्रक्रिया जो तालिका चर में संग्रहीत करती है और उसी प्रारूप के साथ किसी अन्य तालिका में मानों से तुलना करती है। एक अस्थायी तालिका से टेबल चर में बदलने से औसत समय 8ms से 2ms तक बढ़ जाता है, जो कि महत्वपूर्ण है जब इसे दिन भर में कई बार और रात में 100,000 बार एक रात की प्रक्रिया में कहा जाता है।
जेसन गोएमाट

आप तालिका के चर पर आंकड़े क्यों बनाए रखना चाहेंगे? संपूर्ण बिंदु रैम में एक अस्थायी तालिका बनाना है जो क्वेरी समाप्त होने के बाद नष्ट हो जाएगी। परिभाषा के अनुसार, ऐसी तालिका में बनाए गए किसी भी आँकड़े का उपयोग कभी नहीं किया जाएगा। आमतौर पर, तथ्य यह है कि तालिका चर में डेटा जहाँ कहीं भी संभव हो रैम में रहता है, उन्हें किसी भी परिदृश्य में अस्थायी टेबल्स की तुलना में तेज़ बनाता है जहां आपका डेटा एसक्यूएल सर्वर के लिए उपलब्ध रैम की मात्रा से छोटा है (जो कि हमारे एसक्यूएल के लिए 100 जीबी + मेमोरी पूल के इन दिनों में है) सर्वर, लगभग हमेशा है)
ज्योफ ग्रिस्वाल्ड

यह हालांकि विस्तारित संग्रहीत प्रक्रियाओं के लिए काम नहीं करता है। त्रुटि यह है कि मेटाडेटा निर्धारित नहीं किया जा सका क्योंकि 'EXECUTE <processurename> @retval OUTPUT' प्रक्रिया में कथन ... एक विस्तारित संग्रहीत कार्यविधि को आमंत्रित करता है
GSerg

11

ठीक है, यहाँ जिम्हार्क द्वारा प्रोत्साहित किया गया, पुराने एकल हैश तालिका दृष्टिकोण का एक उदाहरण है: -

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

*/

मैंने इस काम को इधर-उधर किया। विचार के लिए आपका धन्यवाद!
SQL_Guy

शानदार काम के आसपास। इससे मुझे टेम्‍प टेबल टेढ़ा होने के बारे में और जानने में मदद मिली। उदाहरण के लिए, मुझे यह महसूस नहीं हुआ कि आप एक dynsql स्ट्रिंग में एक अस्थायी तालिका का उपयोग कर सकते हैं यदि इसे इसके बाहर घोषित किया गया था। इसी तरह की अवधारणा यहाँ। बहुत बहुत धन्यवाद।
jbd

9

इस समस्या के लिए मेरा काम हमेशा इस सिद्धांत का उपयोग करना रहा है कि सिंगल हैश टेम्प टेबल किसी भी प्रॉक्स के दायरे में हैं। इसलिए, मेरे पास खरीद मापदंडों में एक स्विच है (डिफ़ॉल्ट सेट-ऑफ)। यदि इस पर स्विच किया जाता है, तो कॉल किया गया कॉल परिणाम में बनाए गए अस्थायी तालिका में परिणाम सम्मिलित करेगा। मुझे लगता है कि अतीत में मैंने इसे एक कदम आगे ले लिया है और यह कहने के लिए कि क्या कोई हैश तालिका स्कोप में मौजूद है, यह जाँचने के लिए कुछ कोड डाल दिया है, यदि यह कोड डालते हैं, अन्यथा परिणाम सेट लौटाएं। अच्छी तरह से काम करने लगता है - प्रोक्स के बीच बड़े डेटा सेट को पारित करने का सबसे अच्छा तरीका।


1
मुझे यह उत्तर पसंद है और मैं शर्त लगाता हूं कि यदि आप प्रदान करने और उदाहरण के लिए अधिक वोट प्राप्त करेंगे।
जिम्हार्क

मैं वर्षों से ऐसा कर रहा हूं। हालांकि यह एसक्यूएल अज़ूर में अभी भी आवश्यक है?
निक एलन

6

यह ट्रिक मेरे लिए काम करती है।

आपको दूरस्थ सर्वर पर यह समस्या नहीं है, क्योंकि दूरस्थ सर्वर पर, अंतिम कमांड कमांड निष्पादित करने के लिए पिछले कमांड के परिणाम की प्रतीक्षा करता है। यह एक ही सर्वर पर मामला नहीं है।

एक वर्कअराउंड के लिए उस स्थिति को लाभ दें।

यदि आपके पास लिंक्ड सर्वर बनाने की सही अनुमति है, तो इसे करें। लिंक्ड सर्वर के समान सर्वर बनाएँ।

  • SSMS में, अपने सर्वर में लॉग इन करें
  • "सर्वर ऑब्जेक्ट पर जाएं
  • "लिंक किए गए सर्वर" पर राइट क्लिक करें, फिर "न्यू लिंक्ड सर्वर"
  • संवाद पर, अपने लिंक किए गए सर्वर का कोई भी नाम दें: उदा: THISSERVER
  • सर्वर प्रकार "अन्य डेटा स्रोत" है
  • प्रदाता: Microsoft OLE DB प्रदाता SQL सर्वर के लिए
  • डेटा स्रोत: आपका आईपी, यह सिर्फ एक डॉट (।) भी हो सकता है, क्योंकि यह लोकलहोस्ट है
  • टैब "सुरक्षा" पर जाएं और 3 को चुनें "लॉगिन के वर्तमान सुरक्षा संदर्भ का उपयोग करके बनाया जाए"
  • यदि आप चाहें तो आप सर्वर विकल्प (तीसरा टैब) को संपादित कर सकते हैं
  • ओके दबाएं, आपका लिंक किया हुआ सर्वर बन गया है

अब SP1 में आपका Sql कमांड है

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

मेरा विश्वास करो, यह काम करता है यहां तक ​​कि आपके पास SP2 में डायनामिक इंसर्ट भी है


4

मैंने पाया कि चारों ओर एक काम एक प्रॉड्स को टेबल वैल्यू फंक्शन में बदलना है। मुझे एहसास है कि हमेशा संभव नहीं है, और अपनी सीमाओं का परिचय देता है। हालांकि, मैं हमेशा इसके लिए एक अच्छे उम्मीदवार के लिए कम से कम प्रक्रियाओं में से एक को खोजने में सक्षम रहा हूं। मुझे यह समाधान पसंद है, क्योंकि यह समाधान के लिए किसी भी "हैक्स" को पेश नहीं करता है।


लेकिन नुकसान एक अपवाद के साथ एक समस्या है, अगर फ़ंक्शन जटिल है, ठीक है?
मुफ्लिक्स

2

मुझे इस मुद्दे का सामना करना पड़ा जब एक संग्रहीत तालिका में एक टेम्पर्ड प्रोक के परिणामों को आयात करने की कोशिश की गई, और उस संग्रहीत प्रोक को अपने स्वयं के ऑपरेशन के भाग के रूप में एक अस्थायी तालिका में डाला गया। समस्या यह है कि SQL सर्वर एक ही प्रक्रिया को एक ही समय में दो अलग-अलग अस्थायी तालिकाओं को लिखने की अनुमति नहीं देता है।

स्वीकृत OPENROWSET उत्तर ठीक काम करता है, लेकिन मुझे अपनी प्रक्रिया में किसी भी गतिशील SQL या बाहरी OLE प्रदाता का उपयोग करने से बचने की आवश्यकता थी, इसलिए मैं एक अलग मार्ग गया।

एक आसान वर्कअराउंड मैंने पाया कि मेरी संग्रहीत प्रक्रिया में अस्थायी तालिका को टेबल चर में बदलना था। यह बिल्कुल वैसा ही काम करता है जैसा कि उसने एक टेम्‍प टेबल के साथ किया था, लेकिन अब मेरी दूसरी टेम्‍प टेबल इंसर्ट के साथ कोई टकराव नहीं हुआ।

बस टिप्पणी से बाहर निकलने के लिए मुझे पता है कि आप में से कुछ लिखने वाले हैं, मुझे प्रदर्शन हत्यारों के रूप में टेबल वेरिएबल्स को चेतावनी देते हुए ... सभी मैं आपको यह कह सकता हूं कि 2020 में यह टेबल वेरिएबल्स से डरने के लिए लाभांश का भुगतान नहीं करता है । यदि यह 2008 था और मेरे डेटाबेस को 16GB रैम वाले सर्वर पर होस्ट किया गया था और 5400RPM HDDs चला रहा था, तो मैं आपसे सहमत हो सकता हूं। लेकिन यह 2020 है और मेरे पास मेरे प्राथमिक भंडारण के रूप में एक SSD सरणी है और सैकड़ों gigs RAM है। मैं अपनी पूरी कंपनी के डेटाबेस को एक टेबल वैरिएबल में लोड कर सकता था और अभी भी बहुत से रैम के लिए अतिरिक्त है।

तालिका चर मेनू पर वापस आ गए हैं!


1

मेरे पास एक ही मुद्दा और दो या दो से अधिक स्पॉर्ट्स में डुप्लिकेट कोड पर चिंता थी। मैंने "मोड" के लिए एक अतिरिक्त विशेषता जोड़ना समाप्त कर दिया। इसने सामान्य कोड को एक स्प्रो के अंदर मौजूद होने दिया और मोड निर्देशित प्रवाह और स्प्रो के सेट का परिणाम दिया।


1

क्या केवल स्थैतिक तालिका के लिए उत्पादन की दुकान के बारे में? पसंद

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

यह आदर्श नहीं है, लेकिन यह इतना सरल है और आपको सब कुछ फिर से लिखने की आवश्यकता नहीं है।

अद्यतन : पिछले समाधान समानांतर प्रश्नों (async और multiuser एक्सेसिंग) के साथ अच्छी तरह से काम नहीं करता है इसलिए अब Iam अस्थायी तालिकाओं का उपयोग कर रहा है

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

नेस्टेड spGetDataसंग्रहीत प्रक्रिया सामग्री

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

आम तौर पर, आप टेबल्स के साथ एक स्पोक एड-हॉक नहीं बना सकते हैं। आपको अधिक उदाहरणों के साथ अपने उदाहरण पर विस्तार करने की आवश्यकता होगी, क्योंकि यह दृष्टिकोण वास्तव में आसानी से ज्ञात या स्वीकृत नहीं है। इसके अलावा, यह एक स्पैरो निष्पादन से अधिक एक लैम्ब्डा एक्सप्रेशन जैसा दिखता है, जो एएनएसआई-एसक्यूएल लैम्ब्डा एक्सप्रेशन दृष्टिकोण के लिए अनुमति नहीं देता है।
गोल्डबिशॉप

यह काम करता है, लेकिन मैंने पाया कि इसके समानांतर प्रश्नों (async और मल्टी यूजर एक्सेस) के साथ अच्छी तरह से काम नहीं करता है। इसलिए अब अस्थायी तालिका दृष्टिकोण का उपयोग कर Iam। मैंने अपना उत्तर अपडेट कर दिया।
मुफ्लिक्स

1
टेंप टेबल लॉजिक अच्छा है, यह स्पोक संदर्भ था जिससे मैं चिंतित था। स्प्रोक्स को स्वाभाविक रूप से सीधे तौर पर क्वियर नहीं किया जा सकता है। टेबल-वैल्यूड फंक्शन्स को सीधे से क्वेर किया जा सकता है। अपने अपडेट किए गए तर्क के अनुसार, आपको सबसे अच्छा तरीका है टेंप टेबल, सेशन, इंस्टेंस या ग्लोबल, और उस बिंदु पर काम करना।
गोल्डबिशप

0

एक आउटपुट कर्सर चर को आंतरिक सपा में घोषित करें:

@c CURSOR VARYING OUTPUT

फिर उस कर्सर को घोषित करें जिसे आप वापस करना चाहते हैं। फिर कर्सर खोलें। फिर संदर्भ सेट करें:

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

बंद या वास्तविक न करें।

अब एक कर्सर पैरामीटर की आपूर्ति करने वाले बाहरी से आंतरिक सपा को कॉल करें:

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

एक बार जब आंतरिक सपा निष्पादित हो जाता है, तो आपका @cOUTलाने के लिए तैयार है। लूप और फिर बंद करें और निपटाएं।


0

यदि आप अन्य संबंधित तकनीकों जैसे C # का उपयोग करने में सक्षम हैं, तो मेरा सुझाव है कि बिल्ट इन SQL कमांड का उपयोग ट्रांजेक्शन पैरामीटर के साथ करें।

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

मैंने एक साधारण कंसोल ऐप बनाया है जो इस क्षमता को प्रदर्शित करता है जो यहां पाया जा सकता है: https://github.com/hecked12/SQL-Transaction-Using-C-Sharp

संक्षेप में, C # आपको इस सीमा को पार करने की अनुमति देता है जहां आप प्रत्येक संग्रहीत प्रक्रिया के आउटपुट का निरीक्षण कर सकते हैं और उस आउटपुट का उपयोग कर सकते हैं जो आपको पसंद है, उदाहरण के लिए आप इसे किसी अन्य संग्रहीत प्रक्रिया को खिला सकते हैं। यदि आउटपुट ठीक है, तो आप लेनदेन कर सकते हैं, अन्यथा, आप रोलबैक का उपयोग करके परिवर्तनों को वापस कर सकते हैं।


-1

SQL Server 2008 R2 पर, मैं तालिका स्तंभों में एक बेमेल था जो रोलबैक त्रुटि का कारण बना। यह तब दूर हुआ जब मैंने अपने sqlcmd टेबल वेरिएबल को इन्सर्ट-एक्ज़िम स्टेटमेंट द्वारा पॉप्युलेट किया जो मैच के लिए खरीदे हुए थे। यह org_code याद आ रहा था। Windows cmd फ़ाइल में, यह संग्रहीत कार्यविधि का परिणाम लोड करता है और इसका चयन करता है।

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"

ओपी एक त्रुटि के बारे में पूछ रहा था जो नेस्टेड संग्रहीत कार्यविधियों में सम्मिलित-निष्पादित स्टेटमेंट का उपयोग करते समय होती है। आपकी समस्या एक अलग त्रुटि लौटाएगी, जैसे "INSERT बयान के लिए चयन सूची में सम्मिलित सूची से कम आइटम हैं। चयन मानों की संख्या INSERT कॉलम की संख्या से मेल खाना चाहिए।"
लॉसबियर

यह एक चेतावनी के अधिक है कि यह संदेश गलत तरीके से प्राप्त करना संभव है।
user3448451
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.