Question

I have a big *.sql file that I run with Invoke-Sqlcmd and variables to do some tasks, and when I have an issue, it's a pain to debug because of the variables and I want to do logging of the SQL script about to be run.

Is there a reasonably native way to produce the final SQL about to be executed with variable substitution without actually running?

Here's a sample I run to restore a *.bak to various environments for example:

Invoke-Sqlcmd -InputFile $sqlFile -QueryTimeout 0 -ServerInstance 'SQLServer' -Database 'Master' -Variable @("DatabaseBAK=$TransactDB", "Database=$TargetDatabase") -Verbose

SQL File:

USE [master]

print 'Killing connections to database $(Database)'
DECLARE @kill varchar(8000) = '';

SELECT @kill = @kill + 'kill ' + CONVERT(varchar(5), session_id) + ';'
FROM sys.dm_exec_sessions
WHERE database_id  = db_id('$(Database)') 
 and session_id <> @@SPID
EXEC(@kill);
GO
USE [master]

print '[Info] RESTORE DATABASE [$(Database)]'
print '[Info] FROM DISK = N''$(DatabaseBAK)'''
print 'Starting restore-------------------->'
RESTORE DATABASE [$(Database)]
FROM DISK = N'$(DatabaseBAK)'
WITH FILE = 1
    ,NOUNLOAD
    ,REPLACE
    ,STATS = 5
GO
print '<--------------------End restore'

I would like to somehow just output the above with variable substitution the way Invoke-SqlCmd does it.

Was it helpful?

Solution 3

I figured out a clever way with set quoted_identifier off

enter image description here

PowerShell

$sqlFile = "C:\Users\user\Desktop\TestOutput.sql"
$begin = 'set quoted_identifier off print "'
$end = '" set quoted_identifier on'

Invoke-Sqlcmd -InputFile $sqlFile -QueryTimeout 0 -ServerInstance 'Server' -Database 'Database' -Variable @("begin=$begin", "end=$end") -Verbose

$begin = '--Begin'
$end = '--End'

Invoke-Sqlcmd -InputFile $sqlFile -QueryTimeout 0 -ServerInstance 'Server' -Database 'Database' -Variable @("begin=$begin", "end=$end") -Verbose

TSQL

/*
$(begin) is either
    set quoted_identifier off print "
    or
    --Begin
*/

$(begin)
select SALESID from SALESTABLE where SALESID = 'SO-000003'
select SALESID from SALESTABLE where SALESID = 'SO-000004'
$(end)

/*
$(end) is either
    " set quoted_identifier on
    or
    --End
*/

OTHER TIPS

I don't think invoke-sqlcmd can do thi. Regular sqlcmd.exe has the -e parameter to output the batches, although it will not output the "GO" . EG

sqlcmd -e -i restore.sql -v database="foo" databaseBak="bk" > Restore.final.sql

You can put SET NOEXEC ON at the top, or just point it at an dummy instance to generate the script.

Or something like:

sqlcmd -e -Q "set noexec on;`n:r restore.sql" -v database="foo" databaseBak="bk"

Instead of reinventing the wheel, just use dbatools - restore-dbadatabase

it has extensive coverage of all the scenarios that you might ever need.

Note: dbatools also has a better version of invoke-sqlcmd --> invoke-sqlcmd2 with a -ParseGO to parse scripts with GO batch seperators and is more optimized version.

Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top