0

I have seen this question Replace value in XML using SQL and again I am thankful, however, I have a XML file with all the trigger creation scripts but I have not been able to execute it because of the GO.

Partial view of my XML file:

<Krishna>IF 'MY_SERVER' &lt;&gt; @@ServerName THROW 50001, 'Wrong Server!!!',1 </Krishna>
<Krishna>GO</Krishna>
<Krishna>use [DB_02]</Krishna>
<Krishna>GO</Krishna>
<Krishna>IF EXISTS (SELECT 'Radhe' FROM sys.triggers t wHERE t.[name] = 'tgr_repl_AuPair_Insert' AND CASE WHEN t.parent_id = 0 THEN t.parent_id ELSE object_id('[sub].[repl_Aupair]') END  = t.parent_id )
                                 </Krishna>
<Krishna>EXEC('BEGIN DROP TRIGGER [sub].[tgr_repl_AuPair_Insert]  END') </Krishna>
<Krishna>GO</Krishna>
<Krishna></Krishna>
<Krishna>CREATE TRIGGER  [sub].[tgr_repl_AuPair_Insert]</Krishna>
<Krishna>ON  [sub].[repl_Aupair]</Krishna>
<Krishna>FOR  INSERT, UPDATE</Krishna>

when I try to get rid of the GO, replacing it like it is suggested here, I get a different error.

DECLARE @XML3 XML
SELECT @XML3 = (SELECT a.trigger_definition AS Krishna FROM TableBackups.dbo._MMiorelli_20220615_triggerdropping_203144_2 a FOR XML PATH(''))

WHILE @xml3.exist(N'//*[text()="GO"]')=1
BEGIN
    SET @xml3.modify(N'replace value of (//*[text()="GO"]/text())[1] with ""');
END


    exec sp_execXML @dbname=N'APCore'
                   ,@XML=@xml3
                   ,@DEBUG=1
                   ,@PrintOnly=0

enter image description here

this is the way I am executing the commands that are within my XML:

declare @i int = 1

select @sql1 =  ''
SELECT @SQL2 = 'Radhe'

WHILE @sql2 is not null
   begin
      SELECT @sql2 = @XML.value('(/Krishna/text())[sql:variable("@i") cast as xs:int?][1]', 'varchar(max)')

      if @DEBUG=1
         PRINT COALESCE(@sql2,'@SQL2 WAS NULL' + ' -- @I IS ' + CAST(@I AS VARCHAR(5)))

      if @sql2 is not null
         begin 

           SET @sql1 = CAST (@sql1 + @sql2 + @vbCrLf AS NVARCHAR(MAX))
          IF @PrintOnly = 1
             BEGIN

                EXEC sp_DisplayNVarchar @textToDisplay = @SQL2, @debug =0

             END 
          ELSE
             BEGIN

                EXEC (@SQL2)  
             
             END

         end

      SELECT @i = @i + 1
              
      if @i >= @Limit
         SET @sql2 = null
  end

BASICALLY:

each line of the XML is a command

SELECT @sql2 = @XML.value('(/Krishna/text())[sql:variable("@i") cast as xs:int?][1]', 'varchar(max)')

My question is: How could I replace the every GO inside my long script into a new line in my XML?

Every time I meet a GO, that GO is removed but from that place on is a new line in my XML.

this is an example of code and XML that works:

here is the code:

---------------------------------------
----check the data
---------------------------------------
GO
SELECT [@@TRANCOUNT]=@@TRANCOUNT
TRUNCATE TABLE #the_inserts
TRUNCATE TABLE #VICASA477
INSERT INTO  #the_inserts(RADHE1)
SELECT RADHE1='use apcore;' + CHAR(10)+CHAR(13) + 'exec sp_count ' + '''' +  E.AP_NAME2 + ''''
FROM #E E

DECLARE @XML3 XML
SELECT @XML3 = (SELECT #the_inserts.radhe1 AS Krishna FROM #the_inserts FOR XML PATH(''))

INSERT INTO #VICASA477
EXEC sp_execXML @dbname=N'APCore'
                ,@XML=@xml3
                ,@DEBUG=0
                ,@PrintOnly=0

select @XML3
SELECT * FROM #vicasa477
GO

Here is the XML: (partial view but you get the idea)

<Krishna>use apcore;

exec sp_count '[sub].[matchAgreementEmailSent]'</Krishna>
<Krishna>use apcore;

exec sp_count '[sub].[receivedLog]'</Krishna>
<Krishna>use apcore;

exec sp_count '[sub].[repl_Airline]'</Krishna>
<Krishna>use apcore;

exec sp_count '[sub].[repl_Airport]'</Krishna>
<Krishna>use apcore;

exec sp_count '[sub].[repl_ArrivalCalendar]'</Krishna>
<Krishna>use apcore;

exec sp_count '[sub].[repl_Aupair]'</Krishna>
<Krishna>

and here the results: (partial view but you get the idea)

enter image description here

Marcello Miorelli
  • 3,368
  • 4
  • 44
  • 67
  • 2
    GO is not a t-sql statement. It is the default batch separator in SSMS. – Sean Lange Jun 16 '22 at 21:08
  • There is no `GO` in your sample XML. Also I don't understand where you get those results from. – Charlieface Jun 16 '22 at 21:08
  • `GO` this is one line of my XML code – Marcello Miorelli Jun 16 '22 at 21:20
  • it is in the link file I posted – Marcello Miorelli Jun 16 '22 at 21:20
  • @SeanLange not disputing that, but my routines that generate the scripts brings the `go` into play. How do I get rid of it? – Marcello Miorelli Jun 16 '22 at 21:21
  • @Charlieface the results come from running the script that starts with `----check the data` - it shows a XML file without `GO` where each line is a complete command. it runs. My problem is where there are `GO`s – Marcello Miorelli Jun 16 '22 at 21:24
  • As per the question guide, please do not post images of code, data, error messages, etc. - copy or type the text into the question. Please reserve the use of images for diagrams or demonstrating rendering bugs, things that are impossible to describe accurately via text. – Dale K Jun 16 '22 at 21:34
  • I am trying to make sense of your post. The error message you showed states "Incorrect syntax near ')'", which is understandable since the statement being executed is of the form `IF EXISTS(...)` with no following statement to be executed if the IF succeeds. Beyond that, I don't see where this `IF EXISTS(...)` is coming from anywhere in your posted code. – T N Jun 16 '22 at 21:40
  • @TN the statement is fine, the XML with the `GO` isnt! I am interested in the XML, how to make it translate into a set of different sql commands that can be executed, just like I posted in the example, with the picture. thanks for trying to help. – Marcello Miorelli Jun 16 '22 at 21:43
  • I think you need to refine and simplify your question. You posted an error message that doesn't match the code and that you also seem not to care about. You are focusing on GO inside XML, but haven't posted any XML sample data containing a GO. I will give a blind shot in the dark: I believe your xml .modify is matching for the entire string = "GO", when perhaps you wish to match and trim a string (an xml text element containing multiple lines of text) that ends with a newline + "GO". In other words "USE xxxdo-somethingGO" != "GO". – T N Jun 16 '22 at 21:54
  • @TN I even added a partial view of the xml in the question – Marcello Miorelli Jun 16 '22 at 22:22
  • @Larnu I agree, of course – Marcello Miorelli Jun 16 '22 at 22:38

2 Answers2

1

There's no good way to run that script from TSQL. You need to run each batch separately, which can be done by accumulating the lines for each batch, and executing it when you see GO. Stripping GO is a hack which won't work for DDL scripts as many DDL statements must begin a batch, or be the only statement in the batch.

The thing that won't work is the scripts like

use somedb
go
create table foo(id int)

In TSQL if you parse and exec this as:

exec ('use somedb')
exec ('create table foo(id int)')

The database context will be switched inside the first batch, but revert to the original database context at the end of the first dynamic SQL invocation. You simply can't change the database context permanently for the session in dynamic SQL. So your pre-processor must concatenate the database context switch with the subsequent batches. eg

exec ('use somedb
      create table foo(id int)')

Which might work if your script is generated exactly the same and you reliably identify the use database statements. But it's complicated and basically not worth doing. Instead use an external program like sqlcmd or powershell's `invoke-sqlcmd', or even a client program like .NET that can issue top-level batches that permanently change the database context.

And you may find other session-level settings that will have the same problem.

David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • I am glad you understood the problem. Is there any example of `invoke-sqlcmd` in these conditions, that you know of? – Marcello Miorelli Jun 16 '22 at 22:48
  • Why are you starting with a script broken up and embedded in an XML doc? invoke-sqlcmd can work directly with the script as a string or file. – David Browne - Microsoft Jun 16 '22 at 23:05
  • [This post](https://stackoverflow.com/questions/483798/creating-stored-procedure-in-another-database) describes a way to dynamically execute sql (such as a `CREATE PROECEDURE`) after applying a `USE targetdb`. It involves executing dynamic SQL that executes the USE followed by a nested EXEC that includes the remaining command(s). This might be combined with logic that loops through the source, tracks the latest USE, builds up multi-line scripts, and executes the commands when a GO or end of data is reached. – T N Jun 17 '22 at 01:38
1

EDIT: As mentioned by @DavidBrowne, this answer doesn't work if changing the current database with USE is necessary.


You can run this script using a cursor, which executes each batch separately.

To split the batches we need to use XQuery. This is made significantly more complicated by the fact that the batches are separated by the same Krishna node again, rather than each being contained in a separate child node.

DECLARE @sql nvarchar(max), @crsr CURSOR;

SET @crsr = CURSOR FAST_FORWARD READ_ONLY FOR
    SELECT
      x.krsh.query('
      let $go := .
      let $prev := /Krishna[. << $go][text() = "GO"][1]
      return /Krishna[text() != "GO"][. << $go and not(. << $prev)]/text()
      ').value('text()[1]','nvarchar(max)') line
    FROM @xml.nodes('/Krishna[text() = "GO"]') x(krsh);

OPEN @crsr;

GOTO Ftch;
WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC sp_executesql @sql;

ftch:
    FETCH NEXT FROM @crsr
      INTO @sql;
END;

db<>fiddle

The logic runs like this:

  • Use .nodes to grab all Krishna nodes which contain GO.
  • For each of those, we run the following XQuery expression:
  • Assign the current node to $go
  • Find the node previous to this which contains GO.
  • Find all Krishna nodes which do not contain GO, and...
  • ... are located before $go...
  • ... and after $prev (if any).
  • Return the inner text()
  • Concatenate all the text together using .query and .value

Note: This assumes that the final node is always GO.

Marcello Miorelli
  • 3,368
  • 4
  • 44
  • 67
Charlieface
  • 52,284
  • 6
  • 19
  • 43