web-dev-qa-db-ja.com

sqlcmd:outが期待どおりに機能しませんか?

次のコードを検討してください。

DECLARE @db sysname;
DECLARE @filename varchar(260);
DECLARE cur CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT d.name
FROM sys.databases d;
OPEN cur;
FETCH NEXT FROM cur INTO @db
WHILE @@FETCH_STATUS = 0
BEGIN
    SET @filename = 'C:\temp\create_database_' + @db + '.txt';
    :setvar c @filename
    :out $(c)
    PRINT $(c);
    FETCH NEXT FROM cur INTO @db
END
CLOSE cur;
DEALLOCATE cur;

C:\tempにデータベースごとに1つずつ、複数のファイルが作成されることを期待していますが、何も作成されず、SQL Serverからエラーが報告されません。

SQLCMDモードを使用してSSMSでこれを実行しようとしました。また、sqlcmd.exeから実行しようとしました

5
Max Vernon

Sqlcmdモードでこれを行う方法は、スクリプトをスクリプト化するスクリプトをスクリプト化してから、それらのスクリプトを実行することです。これは、InceptionやBorgesのような、夢の中の夢という、少し異なる考え方です。

-- Script a script which scripts a script
SET NOCOUNT ON
GO

:out d:\temp\temp.sql
GO

SELECT REPLACE( ':out d:\temp\@database.sql
-- Do some work here
PRINT ''select ''''@database''''''
GO

-- Return stdout to normal
:out STDOUT
GO
', '@database', name ) AS [--sql]
--C:\temp\create_database_' + name + '.txt'
FROM sys.databases 
GO


-- Return stdout to normal
:out STDOUT
GO

-- Now execute your scripted file
:r d:\temp\temp.sql
GO


-- Create another script which executes all thoses scripts ...
:out d:\temp\temp2.sql
GO

SELECT REPLACE( ':r d:\temp\@database.sql
GO
', '@database', name ) AS [--sql]
FROM sys.databases 
GO

-- Return stdout to normal
:out STDOUT
GO

-- Execute your script of scripts?
:r d:\temp\temp2.sql
GO
3
wBob

このようにSQLCMDを使用するのは初めてのことであり、インターネットで例を探すようになりました。

SQL Server Management Studio:Hacking SQLCMD Mode の情報を調査し、SQLCMD変数の制限について考えた後、問題に対して少し異なるアプローチをとりました。私の解決策は、なぜ:OUTは期待どおりに動作していませんが、とにかく提供します。

私の完全なスクリプトは私の答えの最後にありますが、ここに説明付きの断片があります。


出力をローカルユーザーの一時ディレクトリにリダイレクトすることから始めました

set nocount on

--Redirect output to user temp folder
--On my computer, this was 'C:\Users\uswsh\AppData\Local\Temp'
:OUT $(TEMP)\DBScript.sql

次に、カーソルを呼び出してデータベースのリストを読み取ります。 PRINTコマンドを使用してオペレーティングシステムのechoコマンドを生成し、個々のデータベース作成ファイルを作成しています。カーソル実行全体の出力は、ユーザーの一時フォルダーのDBScript.txtファイルに書き込まれます。

--Declare variables used by cursor
DECLARE @db sysname;
DECLARE cur CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT d.name
FROM sys.databases d;
OPEN cur;
FETCH NEXT FROM cur INTO @db
WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT '!!echo nul > C:\temp\create_database_' + @db + '.txt';
        FETCH NEXT FROM cur INTO @db
    END
CLOSE cur;
DEALLOCATE cur;
go

上記を実行すると、DBScript.txtファイルに次のコマンドが含まれるようになります(各コマンドの先頭にあるダブルバンは、これらをオペレーティングシステムコマンドとして実行するようにSQLCMDに指示します)。

!!echo nul > C:\temp\create_database_master.txt
!!echo nul > C:\temp\create_database_tempdb.txt
!!echo nul > C:\temp\create_database_model.txt
!!echo nul > C:\temp\create_database_msdb.txt
!!echo nul > C:\temp\create_database_Test.txt
!!echo nul > C:\temp\create_database_AdventureWorks2014.txt

次に、元のスクリプトに戻り、SQLCMDの出力をstdoutに戻し、DBScript.txtのコマンドを実行します。

--Revert output to stdout
:OUT stdout

--Execute the script you just created that has the !!echo commands
:r $(TEMP)\DBScript.sql
GO

C:\ Tempディレクトリを見ると、次の個別ファイルがあります。

enter image description here

完全なスクリプトは次のとおりです。

set nocount on

--Redirect output to user temp folder
--On my computer, this was 'C:\Users\uswsh\AppData\Local\Temp'
:OUT $(TEMP)\DBScript.sql

--Declare variables used by cursor
DECLARE @db sysname;
DECLARE cur CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT d.name
FROM sys.databases d;
OPEN cur;
FETCH NEXT FROM cur INTO @db
WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT '!!echo nul > C:\temp\create_database_' + @db + '.txt';
        FETCH NEXT FROM cur INTO @db
    END
CLOSE cur;
DEALLOCATE cur;
go

--Revert output to stdout
:OUT stdout

--Execute the script you just created that has the !!echo commands
:r $(TEMP)\DBScript.sql
GO
4
Scott Hodgin

わかりました、私は here から答えを得ると思います

ここで引用したい

Sqlcmd変数とt-sql変数は、sqlcmdスクリプトの実行の異なる異なるステップで個別に評価されるため、これは不可能です。

これは真実です。別のWordでは、t-sqlが実行される前に:setvar:outが最初に評価されます。また、以下に示す単純なt-sqlがエラーメッセージを表示する理由も説明しています

DECLARE @filename varchar(260)='c:\temp\test.txt';

:setvar c @filename
:out $(c)
PRINT $(c);

enter image description here

ご覧のとおり、エラーメッセージには「出力を@filenameにリダイレクトできません」と「c:\ windows\system32\@filename」が含まれています。これは、:outが評価されるときに@filenameの値がわからないためです。 :outの目的でリテラル文字列@filenameを使用します。これにより、デフォルトで「c:\ windows\system32 \」のデフォルトパスに自動的に設定されます

2
jyao