web-dev-qa-db-ja.com

sqlcmdで4000文字以上を出力する方法

データベースを復元するためのコードを返すストアドプロシージャrestoredatabaseを作成しました。出力スクリプトは次のようになります。

_restore database [DB]  from disk='F:\FULL1.bak' with norecovery
restore database [DB]  from disk='F:\DIFF1.bak' with norecovery
restore log [DB]  from disk='F:\log1.bak' with norecovery
restore log [DB]  from disk='F:\log2.bak' with norecovery
...
restore log [DB]  from disk='F:\logN.bak' with norecovery
restore database [DB] with recovery
_

SPを介して、行はパラメーター@sql varchar(max)に追加され、最後にprint(@sql)を使用して出力されます。ユーザーが正しいフラグを指定した場合は、exec(@sql)。どちらも完全に機能します。ただし、SPをsqlcmdで次のように呼び出すと、

_set restoresql=exec restoredatabase @DB='Databasename', @Execute='y'
sqlcmd -h-1 -S SRV\T2 -U sa -P sa -d master -Q "%restoresql%" -o output.txt
_

出力ファイル_output.txt_は、常に最大4002文字です。

_-y 8000_を追加しようとしましたが、それは役に立ちません。出力ファイルを取得して、生成された完全なスクリプトを取得するにはどうすればよいですか?

6
user1261104

明確にするために、問題は実際にはPRINTコマンドではなく、SQLCMDユーティリティにあります。

PRINTコマンドは、NCHAR/NVARCHARを使用して4000文字に制限されているため、変数は実際にはNVARCHAR(MAX)ではなくVARCHAR(MAX)であると思います。それ以外の場合は、NVARCHAR/CHARを使用して最大8000文字を出力できます。 VARCHARが4000文字を超え、8000文字を超えないことを確認するには、次のコマンドを実行します。

sqlcmd -Q "DECLARE @Yo VARCHAR(MAX) = REPLICATE(CONVERT(VARCHAR(MAX), '#'), 9000); PRINT @Yo;" -o out.txt

8000文字を超える印刷が必要な場合は、最大8000文字のチャンクで変数を反復処理する必要があります(PRINTコマンドは、一度に最大8000個のCHAR/VARCHAR文字のみを表示します)。ただし、単語/オブジェクト名/番号など、分割してはいけないものの真ん中にある可能性があるため、単純に8000文字のチャンクに分割したくない場合があります。PRINTコマンドには常に最後の改行文字。各8000文字のチャンクの最後の改行文字までを印刷するのが最適です。その結果、複数の表示された行に分割される唯一の行は、次の改行文字の前に8000文字を超える行であり、それについて実際にできることは何もありません。

print(@sql)コマンドの代わりに次のプロシージャを試してください(FYI:PRINTの場合のようにEXECの場合、@sqlを括弧で囲む必要はありません)。文字列内の最後の改行文字を見つけるには、T-SQLに組み込まれていないLastIndexOf関数を作成する必要があることに注意してください。これを行うためのT-SQL UDFを作成できます。 SQLCLRを使用することもできます。その場合は、自分でコーディングするか、単純に SQL# ライブラリの無料版(私が作成者です)を入手できます。

SET ANSI_NULLS, QUOTED_IDENTIFIER ON;

IF (OBJECT_ID(N'dbo.Display') IS NOT NULL)
BEGIN
  DROP PROCEDURE dbo.Display;
END;

GO
CREATE PROCEDURE dbo.Display
(
  @TextToDisplay VARCHAR(MAX)
)
AS
SET NOCOUNT ON;

  DECLARE @Length INT = LEN(@TextToDisplay),
          @Buffer VARCHAR(8000),
          @BufferLength INT,
          @StartIndex INT = 1,
          @LastNewlineIndex INT;

  SET @TextToDisplay = REPLACE(@TextToDisplay, CHAR(13), ''); -- normalize

  WHILE (1 = 1)
  BEGIN
    SET @Buffer = SUBSTRING(@TextToDisplay, @StartIndex, 8000);
    SET @BufferLength = DATALENGTH(@Buffer);

    IF (@BufferLength < 8000)
    BEGIN
      BREAK;
    END;

    SET @LastNewlineIndex =
                   SQL#.String_LastIndexOf(@Buffer, CHAR(10), @BufferLength, 1);

    IF (@LastNewlineIndex > 0)
    BEGIN
      PRINT SUBSTRING(@Buffer, 1, (@LastNewlineIndex - 1));
      SET @StartIndex += @LastNewlineIndex;
    END;
    ELSE
    BEGIN
      PRINT @Buffer;
      SET @StartIndex += @BufferLength;
    END;
  END; -- WHILE (1 = 1)

  -- Don't print empty line if final chunk was 8000 chars leaving final loop with 0
  IF (DATALENGTH(@Buffer) > 0)
  BEGIN
    PRINT @Buffer;
  END;
GO

テスト:

DECLARE @Test VARCHAR(MAX);
SET @Test = '';
SET @Test = @Test + '1' + REPLICATE('a', 7998) + '1';
SET @Test = @Test + '2' + REPLICATE('b', 7998) + '2';
SET @Test = @Test + '3' + REPLICATE('c', 7998) + '3';

SELECT @Test;

PRINT '=============================';
EXEC dbo.Display @Test;
PRINT '=============================';
----
SET @Test = '';
SET @Test = @Test + '1' + REPLICATE('a', 7998) + '1';
SET @Test = @Test + '2' + REPLICATE('b', 7998) + '2';
SET @Test = @Test + '3' + REPLICATE('c', 7000) + '3';

SELECT @Test;

PRINT '=============================';
EXEC dbo.Display @Test;
PRINT '=============================';
----
SET @Test = '';
SET @Test = @Test + '1.1' + REPLICATE('a', 7000) + '1.1' + CHAR(13) + CHAR(10);
SET @Test = @Test + '1.2' + REPLICATE('a', 400) + '1.2' + CHAR(13) + CHAR(10);
SET @Test = @Test + '1.3' + REPLICATE('a', 4000) + '1.3' + CHAR(13) + CHAR(10);
SET @Test = @Test + '2' + REPLICATE('b', 1798) + '2' + CHAR(13) + CHAR(10);
SET @Test = @Test + '3' + REPLICATE('c', 7000) + '3';

SELECT @Test;

PRINT '=============================';
EXEC dbo.Display @Test;
PRINT '=============================';

ノート:

  1. あなたは組み込みのCHARINDEX関数を使用して次の各改行を探すためにできます。この点で、文字列分割関数を使用するのとほとんど同じように機能します。 。ここで必要なことと改行で単純な分割を行うことの違いは、8000文字を超える返される要素は、8000文字以下のチャンクに分割する必要があるということです。
  2. NVARCHAR(MAX)の代わりにVARCHAR(MAX)を処理するには、Pastebinに投稿したコードを使用します。 T-SQL Stored Proc to PRINT NVARCHAR(MAX)values
  3. ストアドプロシージャUtil_PrintがSQL#ライブラリにあり、NVARCHAR(MAX)を処理し、上記のT-SQLコード以外の機能を備えていますが、フル(つまり、有償)でのみ使用できます。無料版ではなく、バージョン。ただし、ここに示すコードはほとんどの場合を処理します。
2
Solomon Rutzky