2012-04-26 12 views
7

में पहला कथन होना चाहिए कुछ प्रशासनिक कार्यों के भाग के रूप में, हमारे पास कई टेबल हैं जिन्हें प्रत्येक को ट्रिगर की आवश्यकता होती है। जब कोई ऑब्जेक्ट संशोधित किया जाता है तो ट्रिगर ऑडिट डेटाबेस में ध्वज और दिनांक सेट करेगा। सादगी के लिए, मेरे पास सभी ऑब्जेक्ट्स वाली एक टेबल है जिसे बनाए गए ट्रिगर्स की आवश्यकता होती है।गतिशील एसक्यूएल त्रुटि: 'ट्रिगर बनाएं' एक क्वेरी बैच

मैं प्रत्येक वस्तु के लिए यह करने के लिए कुछ गतिशील एसक्यूएल उत्पन्न करने के लिए कोशिश कर रहा हूँ, लेकिन मैं इस त्रुटि हो रही है:
'CREATE TRIGGER' must be the first statement in a query batch.

यहाँ कोड एसक्यूएल उत्पन्न करने के लिए है।

CREATE PROCEDURE [spCreateTableTriggers] 
AS 

BEGIN 

DECLARE @dbname  varchar(50), 
     @schemaname varchar(50), 
     @objname varchar(150), 
     @objtype varchar(150), 
     @sql  nvarchar(max), 
     @CRLF  varchar(2) 

SET  @CRLF = CHAR(13) + CHAR(10); 

DECLARE ObjectCursor CURSOR FOR 
SELECT DatabaseName,SchemaName,ObjectName 
FROM Audit.dbo.ObjectUpdates; 

SET NOCOUNT ON; 

OPEN ObjectCursor ; 

FETCH NEXT FROM ObjectCursor 
INTO @dbname,@schemaname,@objname; 

WHILE @@FETCH_STATUS=0 
BEGIN 

    SET @sql = N'USE '+QUOTENAME(@dbname)+'; ' 
    SET @sql = @sql + N'IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'''+QUOTENAME(@schemaname)+'.[Tiud_'[email protected]+'_AuditObjectUpdates]'')) ' 
    SET @sql = @sql + N'BEGIN DROP TRIGGER '+QUOTENAME(@schemaname)+'.[Tiud_'[email protected]+'_AuditObjectUpdates]; END; '[email protected] 
    SET @sql = @sql + N'CREATE TRIGGER '+QUOTENAME(@schemaname)+'.[Tiud_'[email protected]+'_AuditObjectUpdates] '[email protected] 
    SET @sql = @sql + N' ON '+QUOTENAME(@schemaname)+'.['[email protected]+'] '[email protected] 
    SET @sql = @sql + N' AFTER INSERT,DELETE,UPDATE'[email protected] 
    SET @sql = @sql + N'AS '[email protected] 
    SET @sql = @sql + N'IF EXISTS(SELECT * FROM Audit.dbo.ObjectUpdates WHERE DatabaseName = '''[email protected]+''' AND ObjectName = '''[email protected]+''' AND RequiresUpdate=0'[email protected] 
    SET @sql = @sql + N'BEGIN'[email protected] 
    SET @sql = @sql + N' SET NOCOUNT ON;'[email protected] 
    SET @sql = @sql + N' UPDATE Audit.dbo.ObjectUpdates'[email protected] 
    SET @sql = @sql + N' SET RequiresUpdate = 1'[email protected] 
    SET @sql = @sql + N' WHERE DatabaseName = '''[email protected]+''' '[email protected] 
    SET @sql = @sql + N'  AND ObjectName = '''[email protected]+''' '[email protected] 

    SET @sql = @sql + N'END' [email protected] 
    SET @sql = @sql + N'ELSE' [email protected] 
    SET @sql = @sql + N'BEGIN' [email protected] 
    SET @sql = @sql + N' SET NOCOUNT ON;' [email protected] 
    SET @sql = @sql + @CRLF 
    SET @sql = @sql + N' -- Update ''SourceLastUpdated'' date.'[email protected] 
    SET @sql = @sql + N' UPDATE Audit.dbo.ObjectUpdates'[email protected] 
    SET @sql = @sql + N' SET SourceLastUpdated = GETDATE() '[email protected] 
    SET @sql = @sql + N' WHERE DatabaseName = '''[email protected]+''' '[email protected] 
    SET @sql = @sql + N'  AND ObjectName = '''[email protected]+''' '[email protected] 
    SET @sql = @sql + N'END; '[email protected] 

    --PRINT(@sql); 
    EXEC sp_executesql @sql; 

    FETCH NEXT FROM ObjectCursor 
    INTO @dbname,@schemaname,@objname; 

END 

CLOSE ObjectCursor ; 
DEALLOCATE ObjectCursor ; 

END 

अगर मैं PRINT का उपयोग करें और एक नई क्वेरी विंडो पर पेस्ट करते हैं, कोड किसी भी समस्या के बिना निष्पादित करता है।

मैंने GO कथन हटा दिए हैं क्योंकि यह त्रुटियां भी दे रहा था।

मुझे क्या याद आ रही है?
मुझे EXEC(@sql); या EXEC sp_executesql @sql; का उपयोग करके त्रुटि क्यों मिल रही है?
क्या यह EXEC() के भीतर संदर्भ के साथ कुछ करना है?
किसी भी मदद के लिए बहुत धन्यवाद।

उत्तर

17

बनाने आप SSMS (या अन्य इसी तरह का उपयोग करते हैं करने के लिए उपकरण) द्वारा उत्पादित कोड को चलाने के लिए यह स्क्रिप्ट, आपको वही त्रुटि मिल जाएगी। जब आप बैच डिलीमीटर (GO) डालते हैं तो यह ठीक हो सकता है, लेकिन अब जब आप नहीं करते हैं, तो आपको एसएसएमएस में भी एक ही समस्या का सामना करना पड़ेगा।

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

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

तो, आप इन विकल्प हैं:

  • डालने EXEC sp_execute @sql के तुरंत बाद ही बात यह है कि ट्रिगर चला जाता है, तो @sql का मान रीसेट तो स्टोर और अपनी बारी में परिभाषा हिस्सा चलाने के लिए;

  • उपयोग दो चर, @sql1 और @sql2, @sql2 में ट्रिगर एक @sql1 में अगर मौजूद है/ड्रॉप हिस्सा स्टोर, बनाएं, फिर (, फिर अलग से) दोनों स्क्रिप्ट चलाते हैं।

लेकिन फिर, जैसा कि आप पहले से ही पता चला है, तो आप एक और मुद्दा सामना करेंगे: आपको लगता है कि डेटाबेस के संदर्भ में बयान चलने के बिना एक और डेटाबेस में एक ट्रिगर नहीं बना सकते।

अब, वहाँ आवश्यक संदर्भ प्रदान करने के 2 तरीके हैं:

1) एक USE बयान का उपयोग करें;

2) EXEC targetdatabase..sp_executesql N'…' का उपयोग कर एक गतिशील क्वेरी के रूप में कथन चलाएं।

जाहिर है, पहला विकल्प यहां काम नहीं करेगा: हम USE …CREATE TRIGGER से पहले नहीं जोड़ सकते हैं, क्योंकि बाद वाले बैच में एकमात्र कथन होना चाहिए।

दूसरा विकल्प इस्तेमाल किया जा सकता है, लेकिन यह dynamicity (यकीन नहीं अगर यह एक शब्द है) की एक अतिरिक्त परत की आवश्यकता होगी। ऐसा इसलिए है क्योंकि डेटाबेस नाम यहां एक पैरामीटर है और इसलिए हमें EXEC targetdatabase..sp_executesql N'…' एक गतिशील स्क्रिप्ट के रूप में चलाने की आवश्यकता है, और चूंकि वास्तविक स्क्रिप्ट चलाने के लिए खुद को गतिशील स्क्रिप्ट माना जाता है, इसलिए, इसे दो बार घोंसला दिया जाएगा।

तो, (द्वितीय) EXEC sp_executesql @sql; लाइन से पहले निम्नलिखित जोड़ें:

SET @sql = N'EXEC ' + @dbname + '..sp_executesql N''' 
      + REPLACE(@sql, '''', '''''') + ''''; 

आप, देखना ठीक से एक नेस्टेड गतिशील स्क्रिप्ट के रूप में @sql की सामग्री को एकीकृत करने के लिए कर सकते हैं, वे एक उद्धरण में संलग्न किया जाना चाहिए। इसी कारण से, प्रत्येक एकल उद्धरण चिह्न @sql को दोगुना किया जाना चाहिए (उदाहरण के लिए उपरोक्त कथन में REPLACE() function का उपयोग करना)।

+0

इसके लिए बहुत धन्यवाद। मैंने कोड को दो 'टुकड़ों' में विभाजित कर दिया है जैसा कि आप ऊपर दिए गए अपने पहले विकल्प में सुझाव देते हैं: – MarkusBee

+0

[पिछली टिप्पणी पर संपादित समय संपादित करें।] बहुत धन्यवाद। जैसा कि आपने अपने पहले विकल्प में सुझाव दिया है, मैंने कोड को दो 'टुकड़ों' में विभाजित कर दिया है। पहला भाग पूरी तरह से निष्पादित करता है। मैं स्पष्ट करता हूं कि प्रक्रिया 'ऑडिट' डेटाबेस से निष्पादित की जाती है और ऑब्जेक्ट्स जिन्हें ट्रिगर की आवश्यकता होती है, अन्य डेटाबेस में होती हैं। 'CREATE TRIGGER' कथन का निष्पादन अब पूरी तरह से योग्य तालिका नाम का उपयोग करते समय निम्न त्रुटि फेंकता है: "ट्रिगर नहीं बना सकता [...] क्योंकि लक्ष्य वर्तमान डेटाबेस में नहीं है।" क्या इसके आसपास कोई रास्ता है? मैं इसे किसी अन्य डेटाबेस के संदर्भ में निष्पादित करने के लिए कैसे प्राप्त कर सकता हूं? धन्यवाद। – MarkusBee

+0

@markb: कृपया मेरा अपडेट देखें। मुझे यकीन नहीं है कि सबकुछ उतना स्पष्ट है जितना मैं चाहता हूं, इसलिए कृपया पूछने में संकोच न करें। –

0

एक ट्रिगर निर्माण अपने निष्पादन बैच पर किया जाना चाहिए। आप एक प्रक्रिया के अंदर हैं ताकि आप इसे बनाने में सक्षम न हों।

मैं एक अस्थायी तालिका में @sql जोड़ने का सुझाव और फिर एक बार proc बयानों के सभी पैदा खत्म कर रहा है, पाश इस अस्थायी तालिका उन्हें अमल और ट्रिगर