sql - गंदा एक संग्रहीत प्रक्रिया से पढ़ता है




sql-server tsql (8)

शुरूआत लेन-देन / प्रतिबद्ध लेनदेन यह सुनिश्चित करेगा कि आपके पास कोई गंदा पढ़ा नहीं है

प्रदर्शन में कोई कमी है, अगर प्रक्रिया एक अन्य हस्तांतरण के अंदर से चलती है, तो सबसे अधिक बाहरी लेन-देन होने तक लेखन लॉक जारी नहीं किया जाएगा। यह सभी थ्रेडों को क्रमबद्ध करेगा और संगामिति को ब्लॉक करेगा।

यह उदाहरण देखें (मान लें कि इसे निष्पादित करने के लिए एक लंबा समय लगता है):

begin tran
    ...
    exec GetNextCerealIdentity ... ; -- the write lock is established
    ...
commit tran -- the write lock is released

लेन-देन के अंत से पहले ताला जारी करना संभव है, लेकिन आपको प्रक्रियाओं का उपयोग करके एक आवेदन लॉक बनाना होगा, स्पा_गेटअपलॉक और स्प_रेलीअनुक्रमणिका GetNextCerealIdentity प्रक्रिया के अंदर लॉक करें

यह काफी मुश्किल हो सकता है, आपको ध्यान देना होगा या आप दोनों को एक गतिरोध या कुछ गंदे पढ़ सकते हैं

आपको अपने कार्यवाही की शुरुआत में और sp_releaseAppLock को अंत में ( spinning back से पहले exec_lock) exec करना चाहिए आपके उदाहरण में आपके पास कई रिटर्न हैं इसलिए लॉक को कई बिंदुओं पर रिलीज करना होगा)

त्रुटियों के मामले में भी लॉक जारी करना मत भूलना ताला लेनदेन के अंत में जारी किया जाएगा, लेकिन आप इसे प्रक्रिया के अंत में जारी करना चाहते हैं! :-)

आपको यह सुनिश्चित करना चाहिए कि आपका एप्लिकेशन लॉक केवल एक ही मेज पर धारण करने वाले (काउंटर) (CfgCerealNumber) है।

आम तौर पर एसक्यूएल सर्वर टेबल पर एक लॉक रखेगा और आपके लॉक में हस्तक्षेप करेगा क्योंकि लिखित लॉक लेन-देन के अंत में जारी किया जाएगा और आपकी प्रक्रिया के अंत में नहीं होगा।

आपको एक लेन-देन स्तर READ UNCOMMITED में प्रक्रिया को बदलना होगा ताकि आपके कोड में अपडेट लिखित लॉक उत्पन्न न करें। जब आप एप्लिकेशन लॉक जारी करते हैं, तो उसी समय में कमेटी पर वापस जाने के लिए याद रखें।

यदि आप अनन्य मोड में ताला लगाते हैं तो आप सुनिश्चित होंगे कि केवल एक कनेक्शन अपडेट निष्पादित करने में सक्षम होगा / तालिका CfgCerealNumber पर चयन करें

आप किसी भी नाम को लॉक दे सकते हैं जिसे आप चाहते हैं। मैंने मेज के रूप में एक ही नाम का प्रयोग किया (सीएफजीसीरेनल नंबर) लेकिन यह महत्वपूर्ण नहीं है सबसे महत्वपूर्ण बात यह है कि आपको आरंभिक प्राप्त करने के लिए और आपके कोड में रखे सभी रिलीज़ के लिए उसी नाम का उपयोग करना होगा।

ALTER procedure GetNextCerealIdentity(@NextKey int output,@TableID int)
AS
declare @RowCount int, @Err int
set nocount on
select  @NextKey = 0

-- replace begin tran with:    
EXEC sp_getapplock @Resource = 'CfgCerealNumber', @LockMode = 'Exclusive';  
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

/*Update CfgCerealNumber Table */
UPDATE CfgCerealNumber Set CerealNumber = CerealNumber + 1  
WHERE CerealNumberID = @TableID
select  @RowCount = @@RowCount, @Err = @@Error  /*Obtain updated Cereal number previously incremented*/

if @Err <> 0   /* If Error gets here then exit         */
    begin                        
    raiserror ('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 
           16,1, @Err, @TableID)
    -- replace Rollback Transaction with:
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    EXEC sp_releaseapplock @Resource = 'CfgCerealNumber';  
    set nocount off
    return 1
    end

if @RowCount = 0 /* No Record then assume table is not   */
                 /* been initialized for TableID Supplied*/
    begin
    raiserror ('No Table Record Exists in CfgCerealNumber for ID:%d   ', 
                  16,1, @TableID)
    set nocount off

    -- replace Rollback Transaction with:
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    EXEC sp_releaseapplock @Resource = 'CfgCerealNumber';  

    return 1
    end

/*Obtain updated Cereal number previously incremented*/
SELECT @NextKey = CerealNumber 
 From CfgCerealNumber WHERE CerealNumberID = @TableID

select   @Err = @@Error /*Obtain updated Cereal number previously incremented*/

if @Err <> 0  /* If Error gets here then exit         */
    begin                        
    raiserror ('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 
           16,1, @Err, @TableID)
    -- replace Rollback Transaction with:
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    EXEC sp_releaseapplock @Resource = 'CfgCerealNumber';  
    set nocount off
    return 1
    end

-- replace commit transaction with:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
EXEC sp_releaseapplock @Resource = 'CfgCerealNumber';  

set nocount off
return 0
GO

यदि आप उस प्रक्रिया को बदलते हैं, तो मेरा पिछला उदाहरण संगामिति के साथ समस्याओं को नहीं देगा:

begin tran
    ...
    exec GetNextCerealIdentity ... ; -- the lock is established AND released
    ...
commit tran -- common "write locks" are released

एक संभावित जोड़ BEGIN / END प्रयास का उपयोग करना है .. BEGIN / END कैच निर्माण, ताकि आप अप्रत्याशित अपवादों के मामले में भी लॉक जारी कर सकते हैं (यह अन्य समर्थक देगा: आपके पास प्रक्रिया से निकास का एक बिंदु होगा आपके पास एक ऐसा बिंदु होगा जहां आपको लॉक जारी करने के लिए निर्देशों को लगाया जाना चाहिए और पिछले लेनदेन अलगाव स्तर को वापस रख दिया जाएगा।

निम्न लिंक देखें: (sp_getAppLock) https://msdn.microsoft.com/en-us/library/ms189823.aspx और (sp_releaseAppLock) https://technet.microsoft.com/en-us/library/ms178602.aspx

मेरे पास 100 धागे हैं जो प्रत्येक नीचे संग्रहीत कार्यविधि को बुलाते हैं।

मैं गंदी पढ़ता कैसे रोकूं?

SET QUOTED_IDENTIFIER OFF
SET ANSI_NULLS OFF
GO

ALTER procedure GetNextCerealIdentity
    (@NextKey int output, @TableID int)
AS
    declare @RowCount int, @Err int

    set nocount on

    select  
        @NextKey = 0

    begin transaction

Again:
    /*Update CfgCerealNumber Table */
    UPDATE CfgCerealNumber 
    SET CerealNumber = CerealNumber + 1  
    WHERE CerealNumberID = @TableID

    SELECT 
        @RowCount = @@RowCount, 
        @Err = @@Error      /*Obtain updated Cereal number previously incremented*/

    IF @Err <> 0            /* If Error gets here then exit         */
    BEGIN
        RAISERROR ('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 16, 1, @Err, @TableID)
        ROLLBACK TRANSACTION

        set nocount off
        return 1
    END

    IF @RowCount = 0                /* No Record then assume table is not   */
                                /* been initialized for TableID Supplied*/
    BEGIN
        RAISERROR('No Table Record Exists in CfgCerealNumber for ID:%d   ', 16, 1, @TableID)
        set nocount off
        Rollback Transaction
        return 1
    END

    /*Obtain updated Cereal number previously incremented*/
    SELECT @NextKey = CerealNumber 
    FROM CfgCerealNumber 
    WHERE CerealNumberID = @TableID

    SELECT @Err = @@Error                       /*Obtain updated Cereal number previously incremented*/

    IF @Err <> 0                            /* If Error gets here then exit         */
    BEGIN
        RAISERROR('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 16, 1, @Err, @TableID)
        Rollback Transaction    
        set nocount off
        return 1
    END

    commit transaction
    set nocount off
    return 0
GO

ऐसा लगता है कि संग्रहीत कार्यविधि के इस भाग के समान मूल्य वापस 0.01% के आसपास लौटते समय समानांतर में चलते हैं:

SELECT @NextKey = CerealNumber 
FROM CfgCerealNumber 
WHERE CerealNumberID = @TableID

मैंने अपने कोड को ऐसे तरीके से संरचित कर दिया है ताकि लेनदेन में अद्यतन लपेटकर गंदी पढ़ा जाए।

मैं गंदी पढ़ता कैसे रोकूं?


sp_getapplock यह सुनिश्चित करेगा कि लेनदेन के पास एक अनन्य ताला है अगली धागे इसका उपयोग करने से पहले अद्यतन और पढ़ता है, इसलिए कोई गंदा पढ़ा नहीं हो सकता।

SET QUOTED_IDENTIFIER OFF
SET ANSI_NULLS OFF
GO

    ALTER procedure GetNextCerealIdentity(@NextKey int output,@TableID int)
    AS
    declare @RowCount int, @Err int
    set nocount on
    select  @NextKey = 0
    begin transaction
   --ADDED CODE
    EXEC sp_getapplock @Resource='MyLock', @LockMode='Exclusive'
                , @LockOwner='Transaction', @LockTimeout = 15000
    Again:
    /*Update CfgCerealNumber Table */
    UPDATE CfgCerealNumber Set CerealNumber = CerealNumber + 1  WHERE CerealNumberID = @TableID
    select  @RowCount = @@RowCount, @Err = @@Error      /*Obtain updated Cereal number previously incremented*/

    if @Err <> 0                            /* If Error gets here then exit         */
        begin                        
        raiserror ('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 
               16,1, @Err, @TableID)
                Rollback Transaction    
        set nocount off
        return 1
        end

    if @RowCount = 0                        /* No Record then assume table is not   */
                                    /* been initialized for TableID Supplied*/
        begin
        raiserror ('No Table Record Exists in CfgCerealNumber for ID:%d   ', 
                      16,1, @TableID)
        set nocount off
                Rollback Transaction
        return 1
        end

    /*Obtain updated Cereal number previously incremented*/
    SELECT @NextKey = CerealNumber 
     From CfgCerealNumber WHERE CerealNumberID = @TableID

    select   @Err = @@Error                     /*Obtain updated Cereal number previously incremented*/

    if @Err <> 0                            /* If Error gets here then exit         */
        begin                        
        raiserror ('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 
               16,1, @Err, @TableID)
                Rollback Transaction    
        set nocount off
        return 1
        end

    commit transaction
    set nocount off
    return 0

आप किताबें ऑनलाइन में वर्णित के रूप में @variable = column = expression वाक्यविन्यास का उपयोग करके समस्या से बच सकते हैं। इसके अलावा, चूंकि स्टेटमेंट एक एकल-स्टेटमेंट स्वचालित लेन-देन में कार्यान्वित करता है, इसलिए आप स्पष्ट लेनदेन से बच सकते हैं।

SET QUOTED_IDENTIFIER ON;
SET ANSI_NULLS ON;
GO

CREATE PROCEDURE GetNextSerialIdentity
      @NextKey int output
    , @TableID int
AS
SET NOCOUNT ON;

UPDATE dbo.CfgSerialNumber
SET @NextKey = SerialNumber = SerialNumber + 1
WHERE SerialNumberID = @TableID;

IF @@ROWCOUNT = 0
BEGIN
RAISERROR ('No Table Record Exists in CfgCerealNumber for ID:%d   ', 
                  16,1, @TableID);
END
GO

आपको इस कथन को बदलने की जरूरत है

UPDATE CfgCerealNumber Set CerealNumber = CerealNumber + 1  
WHERE CerealNumberID = @TableID

इसके द्वारा:

declare @CerealNumber int

SELECT @CerealNumber = CerealNumber  + 1
FROM CfgCerealNumber WITH (READCOMMITTED, READPAST, ROWLOCK) 
WHERE CerealNumberID = @TableID

if @CerealNumber is not null
    UPDATE CfgCerealNumber Set CerealNumber = @CerealNumber
    WHERE CerealNumberID = @TableID
else
    raiserror ('Row was locked by another update (no dirty read and no deadlock happen) or no Table Record Exists in CfgCerealNumber for ID:%d   ', 
              16,1, @TableID)

ये तालिका संकेतक, रीडैप, संकेतक, सुनिश्चित करें कि आपके पास गंदे पढ़ने और कोई गतिरोध नहीं है

यह भी आपको यह तय करने देगा कि क्या आप अभी भी अपडेट करना चाहते हैं

READCOMMITTED
निर्दिष्ट करता है कि रीड कमेटेड अलगाव स्तर के नियमों का पालन करते हुए ऑपरेशन को पढ़ने या लॉकिंग या पंक्ति संस्करण का उपयोग करके यदि डेटाबेस विकल्प READ_COMMITTED_SNAPSHOT बंद हो गया है, तो डाटाबेस इंजन साझा लॉक प्राप्त करता है क्योंकि डेटा को पढ़ा जाता है और पढ़ने के कार्य पूर्ण होने पर उन तालों को रिलीज़ किया जाता है। यदि डेटाबेस विकल्प READ_COMMITTED_SNAPSHOT चालू है, तो डेटाबेस इंजिन लॉक हासिल नहीं करता है और पंक्ति संस्करण का उपयोग करता है।

READPAST
निर्दिष्ट करता है कि डेटाबेस इंजिन अन्य लेनदेन द्वारा लॉक किए गए पंक्तियों को नहीं पढ़ता है। जब READPAST निर्दिष्ट होता है, पंक्ति-स्तर के ताले छोड़ दिए जाते हैं। यही है, डाटाबेस इंजन मौजूदा लेनदेन को अवरुद्ध करने के बजाय पंक्तियों के पीछे रुक जाता है जब तक ताले जारी नहीं होते हैं। उदाहरण के लिए, तालिका T1 में 1, 2, 3, 4, 5 के मान के साथ एक पूर्णांक स्तंभ होता है। यदि लेनदेन ए 3 से 8 का मान बदलता है, लेकिन अभी तक प्रतिबद्ध नहीं है, तो एक SELECT * FROM T1 (READPAST) पैदावार 1, 2, 4, 5 के मान। रीडैप का उपयोग मुख्य रूप से एक SQL कतार का उपयोग करने वाले कार्य कतार को लागू करते समय ताला लगा विवाद को कम करने के लिए किया जाता है। एक कतार पाठक जो रीडाएप का उपयोग करता है, वह अगली उपलब्ध कतार प्रविष्टि में अन्य लेनदेन द्वारा लॉक की गई कतार प्रविष्टियों को छोड़ देता है, जब तक कि अन्य लेन-देन अपने ताले जारी नहीं होने तक इंतजार किए बिना।

चप्पू-आंकड़ा
निर्दिष्ट करता है कि जब पेज या टेबल लॉक को आमतौर पर लिया जाता है तो पंक्ति ताले उठाए जाते हैं। जब SNAPSHOT अलगाव स्तर पर काम कर रहे लेनदेन में निर्दिष्ट किया जाता है, तब तक पंक्ति ताले नहीं ले जाते हैं जब तक कि ROWLOCK को अन्य तालिका संकेतों के साथ जोड़ा न जाए, जिसकी आवश्यकता होती है ताले, जैसे UPDLOCK और HOLDLOCK

स्रोत एमएसडीएन टेबल संकेत (ट्रांसएक्ट-एसक्यूएल)

आपको UPDLOCK और / या HOLDLOCK का उपयोग करने की आवश्यकता हो सकती है


अगर आपको अपडेट करने और अपडेट करने की आवश्यकता है, तो मैं केवल OUTPUT खंड का उपयोग करेगा:

UPDATE CfgCerealNumber 
SET CerealNumber = CerealNumber + 1 
OUTPUT INSERTED.CerealNumber
WHERE CerealNumberID = @TableID;

यदि आपको अतिरिक्त जाँच की आवश्यकता है, तो आप संग्रहीत कार्यविधि से परिणाम सेट को वापस करने से पहले एक घोषित तालिका चर में OUTPUT कर सकते हैं।

एक अन्य विकल्प पहले मेज पर ब्लॉकिंग लॉक बनाना होगा, और फिर अपडेट करें:

SELECT @CerealNumber = CerealNumber + 1 
FROM CfgCerealNumber WITH (HOLDLOCK, UPDLOCK) 
WHERE CerealNumberID = @TableID;

UPDATE CfgCerealNumber
SET CerealNumber = @CerealNumber
WHERE CerealNumberID = @TableID;

लेकिन मैं पैसे नीचे डालूँगा कि मैंने इसे अभी भी समस्याएं पैदा की हैं। मुझे विश्वास है कि यह बहुत कम है


जैसा कि पहले से उल्लेख किया गया है, आप स्वत: वृद्धि वृद्धि की कार्यक्षमता का उपयोग कर सकते हैं, जैसे पहचान स्तंभ या अनुक्रम।

यदि आप यह नहीं चाहते हैं, तो आपको सीरीयल तरीके से तालिका तक पहुंच बनाने की आवश्यकता है: एप्लिकेशन लॉक या अन्य क्षमताओं का उपयोग करना।

उदाहरण के लिए, आप इशारों को तालिका में पहली पहुंच (लेनदेन में) जैसे नीचे जोड़ सकते हैं:

UPDATE CfgCerealNumber
Set CerealNumber = CerealNumber + 1
FROM CfgCerealNumber with (tablockx, holdlock)
WHERE CerealNumberID = @TableID

यह तालिका के सभी समानांतर थ्रेड्स में अनुक्रमिक पहुंच की गारंटी देगा।


बेकन बिट्स ने मुझे इसे मार दिया, लेकिन OUTPUT क्लॉज का उपयोग करना आपके रेसिंग मुद्दे के आसपास काम करने का सबसे आसान तरीका होगा। ऑफ लॉकिंग बंद करना एक विकल्प भी है, हालांकि मुझे लगता है कि इसमें थोड़ा अधिक ऊंचा होगा उस ने कहा है, एक IDENTITY स्तंभ या एक SEQUENCE का उपयोग इस कार्यक्षमता मैन्युअल रूप से लागू करने की कोशिश करने से इतना आसान है।

मैंने आपके कोड में जवाब डालने की स्वतंत्रता ली और कुछ टिप्पणियां जोड़ें:

SET QUOTED_IDENTIFIER OFF
SET ANSI_NULLS OFF
GO

ALTER procedure GetNextCerealIdentity(@NextKey int output,@TableID int)
AS
set nocount on

DECLARE @RowCount int, @Err int
DECLARE @output TABLE (NextKey int)

begin transaction

    /*Update CfgCerealNumber Table */
    UPDATE CfgCerealNumber WITH (UPDLOCK) 
       Set CerealNumber = CerealNumber + 1
    OUTPUT inserted.CerealNumber INTO @output (NextKey)
     WHERE CerealNumberID = @TableID

    select @RowCount = @@RowCount, /*Obtain updated Cereal number previously incremented*/ 
           @Err = @@Error      

    if @Err <> 0                            /* If Error gets here then exit         */
        begin                        
            Rollback Transaction    
            raiserror ('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 16,1, @Err, @TableID)
            return -1
        end

    if @RowCount = 0                        /* No Record then assume table is not   */
                                    /* been initialized for TableID Supplied*/
        begin
            Rollback Transaction
            raiserror ('No Table Record Exists in CfgCerealNumber for ID:%d   ', 16,1, @TableID)
            return -1
        end

COMMIT TRANSACTION


/*Obtain updated Cereal number previously incremented*/
SELECT @NextKey = NextKey 
 From @output

return 0
GO

टिप्पणियों:

  • संग्रहीत कार्यविधि से बाहर निकलने से पहले SET NOCOUNT OFF फिर से करने की कोई आवश्यकता नहीं है। जैसा कि आप दायरे से बाहर निकलते हैं, यह सेटिंग आपके द्वारा संग्रहीत कार्यप्रणाली को दर्ज करने से पहले वापस लौटा दी जाएगी।
  • मुझे यकीन नहीं है कि आपको WITH (UPDLOCK) ज़रूरत है, लेकिन यह निश्चित रूप से चोट नहीं पहुंचेगी।
  • मैंने लेनदेन को यथासंभव कम समय तक खुला रखा है, लेन-देन के अंदर तालिका-चर से मूल्य लाने का कोई कारण नहीं है।
  • मुझे लगता है कि पहले ROLLBACK करना सुरक्षित है और फिर एक RaisError() क्योंकि बाद में कुछ क्लाइंट सॉफ़्टवेयर द्वारा कनेक्शन को छोड़ा जा सकता है और / या आप TRY...CATCH । दोनों आज्ञाओं के प्रवाह को तोड़ देंगे और आप लेन-देन संख्या के साथ समाप्त हो जाएगा।
  • वाईएमएमवी लेकिन मुझे हमेशा एक त्रुटि के मामले में नकारात्मक रिटर्न कोड का उपयोग करने के लिए कहा गया है सकारात्मक वापसी-कोड का इस्तेमाल पंक्तियों की संख्या को इंगित करने के लिए किया जा सकता है .. हालांकि मैंने कभी इसे कभी अभ्यास में इस्तेमाल नहीं किया है।

/* 
    SEARCH SPROCS & VIEWS

    The following query will allow search within the definitions 
    of stored procedures and views.

    It spits out the results as XML, with the full definitions, 
    so you can browse them without having to script them individually.

*/

/*
   STEP 1: POPULATE SEARCH KEYS. (Set to NULL to ignore)
*/
DECLARE 
    @def_key varchar(128) = '%foo%',      /* <<< definition search key */
    @name_key varchar(128) = '%bar%',     /* <<< name search key       */
    @schema_key varchar(128) = 'dbo';     /* <<< schema search key     */

;WITH SearchResults AS (
    /* 
       STEP 2: DEFINE SEARCH QUERY AS CTE (Common Table Expression)
    */
    SELECT 
        [Object].object_id                       AS [object_id],    
        [Schema].name                            AS [schema_name], 
        [Object].name                            AS [object_name],
        [Object].type                            AS [object_type],
        [Object].type_desc                       AS [object_type_desc],
        [Details].definition                     AS [module_definition]
    FROM  
        /* sys.sql_modules = where the body of sprocs and views live */
        sys.sql_modules AS [Details] WITH (NOLOCK)
    JOIN
        /* sys.objects = where the metadata for every object in the database lives */
        sys.objects AS [Object] WITH (NOLOCK) ON [Details].object_id = [Object].object_id
    JOIN 
        /* sys.schemas = where the schemas in the datatabase live */
        sys.schemas AS [Schema] WITH (NOLOCK) ON [Object].schema_id = [Schema].schema_id
    WHERE 
        (@def_key IS NULL OR [Details].definition LIKE @def_key)      /* <<< searches definition */
        AND (@name_key IS NULL OR [Object].name LIKE @name_key)       /* <<< searches name       */
        AND (@schema_key IS NULL OR [Schema].name LIKE @schema_key)   /* <<< searches schema     */
)
/* 
   STEP 3: SELECT FROM CTE INTO XML
*/

/* 
    This outer select wraps the inner queries in to the <sql_object> root element 
*/
SELECT 
(
    /* 
        This inner query maps stored procedure rows to <procedure> elements
    */
    SELECT TOP 100 PERCENT
        [object_id]                            AS [@object_id], 
        [schema_name] + '.' + [object_name]    AS [@full_name],
        [module_definition]                    AS [module_definition]
    FROM
        SearchResults
    WHERE
        object_type = 'P'
    ORDER BY
        [schema_name], [object_name]
    FOR XML
        PATH ('procedure'), TYPE
) AS [procedures],  /* <<< as part of the outer query, 
                           this alias causes the <procedure> elements
                           to be wrapped within the <procedures> element */
(
    /* 
        This inner query maps view rows to <view> elements
    */
    SELECT TOP 100 PERCENT 
        [object_id]                            AS [@object_id], 
        [schema_name] + '.' + [object_name]    AS [@full_name],
        [module_definition]                    AS [module_definition]
    FROM
        SearchResults
    WHERE
        object_type = 'V'
    ORDER BY
        [schema_name], [object_name]
    FOR XML
        PATH ('view'), TYPE
) AS [views]  /* <<< as part of the outer query, 
                     this alias causes the <view> elements
                     to be wrapped within the <views> element */
FOR XML 
    PATH ('sql_objects')






sql sql-server tsql