web-dev-qa-db-ja.com

Printステートメントを使用してVARCHAR(MAX)を印刷する方法は?

私は次のようなコードを持っています:

DECLARE @Script VARCHAR(MAX)

SELECT @Script = definition FROM manged.sys.all_sql_modules sq
where sq.object_id = (SELECT object_id from managed.sys.objects 
Where type = 'P' and Name = 'usp_gen_data')

Declare @Pos int

SELECT  @pos=CHARINDEX(CHAR(13)+CHAR(10),@script,7500)

PRINT SUBSTRING(@Script,1,@Pos)

PRINT SUBSTRING(@script,@pos,8000)

スクリプトの長さは約10,000文字で、最大8000文字しか保持できないprintステートメントを使用しているため、2つのprintステートメントを使用しています。

問題は、たとえば18000文字のスクリプトがあるときに、3つの印刷ステートメントを使用していたことです。

スクリプトの長さに応じて印刷ステートメントの数を設定する方法はありますか?

91
peter

スクリプトの長さのカウントを8000で割った値に基づいて、WHILEループを実行できます。

例えば:

DECLARE @Counter INT
SET @Counter = 0
DECLARE @TotalPrints INT
SET @TotalPrints = (LEN(@script) / 8000) + 1
WHILE @Counter < @TotalPrints 
BEGIN
    -- Do your printing...
    SET @Counter = @Counter + 1
END
17
Kelsey

私はそれが古い質問であることを知っていますが、私がしたことはここでは言及されていません。

私にとっては、以下がうまくいきました。

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)
182
alfoks

次の回避策では、PRINTステートメントを使用しません。 SQL Server Management Studioと組み合わせて使用​​すると効果的です。

SELECT CAST('<root><![CDATA[' + @MyLongString + ']]></root>' AS XML)

返されたXMLをクリックして、組み込みのXMLビューアーで展開できます。

表示されるサイズにはかなり寛大なクライアント側の制限があります。必要に応じて、Tools/Options/Query Results/SQL Server/Results to Grid/XML dataに移動して調整します。

84
Jirka Hanika

これを行う方法を次に示します。

DECLARE @String NVARCHAR(MAX);
DECLARE @CurrentEnd BIGINT; /* track the length of the next substring */
DECLARE @offset tinyint; /*tracks the amount of offset needed */
set @string = replace(  replace(@string, char(13) + char(10), char(10))   , char(13), char(10))

WHILE LEN(@String) > 1
BEGIN
    IF CHARINDEX(CHAR(10), @String) between 1 AND 4000
    BEGIN
           SET @CurrentEnd =  CHARINDEX(char(10), @String) -1
           set @offset = 2
    END
    ELSE
    BEGIN
           SET @CurrentEnd = 4000
            set @offset = 1
    END   
    PRINT SUBSTRING(@String, 1, @CurrentEnd) 
    set @string = SUBSTRING(@String, @CurrentEnd+@offset, LEN(@String))   
END /*End While loop*/

http://ask.sqlservercentral.com/questions/3102/any-way-around-the-print-limit-of-nvarcharmax-in-s.html から取得

36
Ben B

この質問に出くわし、もっとシンプルなものが欲しかった...次を試してください:

SELECT [processing-instruction(x)]=@Script FOR XML PATH(''),TYPE
10
Edyn

このプロシージャは、ラッピングを考慮してVARCHAR(MAX)パラメータを正しく出力します。

CREATE PROCEDURE [dbo].[Print]
    @sql varchar(max)
AS
BEGIN
    declare
        @n int,
        @i int = 0,
        @s int = 0, -- substring start posotion
        @l int;     -- substring length

    set @n = ceiling(len(@sql) / 8000.0);

    while @i < @n
    begin
        set @l = 8000 - charindex(char(13), reverse(substring(@sql, @s, 8000)));
        print substring(@sql, @s, @l);
        set @i = @i + 1;
        set @s = @s + @l + 2; -- accumulation + CR/LF
    end

    return 0
END
10
Andrey Morozov

ほとんどの人が似たような理由でprintを使用していることを想像して、printステートメントを使用していくつかの動的SQLをデバッグしようとしていました。

リストされたソリューションのいくつかを試したところ、ケルシーのソリューションはマイナーな週(@sqlは私の@script)で動作することがわかりました。 LENGTHは有効な関数ではありません:

--http://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement
--Kelsey
DECLARE @Counter INT
SET @Counter = 0
DECLARE @TotalPrints INT
SET @TotalPrints = (LEN(@sql) / 4000) + 1
WHILE @Counter < @TotalPrints 
BEGIN
    PRINT SUBSTRING(@sql, @Counter * 4000, 4000)
    SET @Counter = @Counter + 1
END
PRINT LEN(@sql)

このコードはコメントされているとおり、出力に新しい行を追加しますが、デバッグのためにこれは問題ではありません。

ベンBのソリューションは完璧で最も洗練されていますが、デバッグには多くのコード行があるため、ケルシーのわずかな変更を使用することを選択します。再利用して1行で呼び出すことができるBen Bのコード用に、msdbにストアドプロシージャのようなシステムを作成する価値があるかもしれません。

残念ながら、Alfoksのコードは簡単に機能しませんでした。

7
Matthew Radford
 create procedure dbo.PrintMax @text nvarchar(max)
 as 
 begin 
 declare @i int、@newline nchar(2)、@print varchar(max ); 
 set @newline = nchar(13)+ nchar(10); 
 select @i = charindex(@newline、@text); 
 while(@i> 0)
 begin 
 select @print = substring(@ text、0、@ i); 
 while(len(@print)> 8000)
 begin 
 print substring(@ print、0,8000); 
 select @print = substring(@ print、8000、len(@print)); 
 end 
 print @print; 
 select @text = substring(@ text、@ i + 2、len(@text)); 
 select @i = charindex(@newline、@text); 
 end 
 print @text; 
 end 
3
Adam Gering

これを使用できます

declare @i int = 1
while Exists(Select(Substring(@Script,@i,4000))) and (@i < LEN(@Script))
begin
     print Substring(@Script,@i,4000)
     set @i = @i+4000
end
3
Marwan Almukh

改行とスペースを適切なブレークポイントとして使用します。

declare @sqlAll as nvarchar(max)
set @sqlAll = '-- Insert all your sql here'

print '@sqlAll - truncated over 4000'
print @sqlAll
print '   '
print '   '
print '   '

print '@sqlAll - split into chunks'
declare @i int = 1, @nextspace int = 0, @newline nchar(2)
set @newline = nchar(13) + nchar(10)


while Exists(Select(Substring(@sqlAll,@i,3000))) and (@i < LEN(@sqlAll))
begin
    while Substring(@sqlAll,@i+3000+@nextspace,1) <> ' ' and Substring(@sqlAll,@i+3000+@nextspace,1) <> @newline
    BEGIN
        set @nextspace = @nextspace + 1
    end
    print Substring(@sqlAll,@i,3000+@nextspace)
    set @i = @i+3000+@nextspace
    set @nextspace = 0
end
print '   '
print '   '
print '   '
2
BickiBoy

または単に:

PRINT SUBSTRING(@SQL_InsertQuery, 1, 8000)
PRINT SUBSTRING(@SQL_InsertQuery, 8001, 16000)
2
Yovav

Bennett Dillによって書かれたPrintMaxと呼ばれる素晴らしい機能があります。

「スキーマ汚染」を回避するために一時ストアドプロシージャを使用するわずかに変更されたバージョンがあります( https://github.com/Toolien/sp_GenMerge/blob/master/sp_GenMerge.sql

EXEC (N'IF EXISTS (SELECT * FROM tempdb.sys.objects 
                   WHERE object_id = OBJECT_ID(N''tempdb..#PrintMax'') 
                   AND type in (N''P'', N''PC''))
    DROP PROCEDURE #PrintMax;');
EXEC (N'CREATE PROCEDURE #PrintMax(@iInput NVARCHAR(MAX))
AS
BEGIN
    IF @iInput IS NULL
    RETURN;

    DECLARE @ReversedData NVARCHAR(MAX)
          , @LineBreakIndex INT
          , @SearchLength INT;

    SET @SearchLength = 4000;

    WHILE LEN(@iInput) > @SearchLength
    BEGIN
    SET @ReversedData = LEFT(@iInput COLLATE DATABASE_DEFAULT, @SearchLength);
    SET @ReversedData = REVERSE(@ReversedData COLLATE DATABASE_DEFAULT);
    SET @LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13),
                          @ReversedData COLLATE DATABASE_DEFAULT);
    PRINT LEFT(@iInput, @SearchLength - @LineBreakIndex + 1);
    SET @iInput = RIGHT(@iInput, LEN(@iInput) - @SearchLength 
                        + @LineBreakIndex - 1);
    END;

    IF LEN(@iInput) > 0
    PRINT @iInput;
END;');

DBFiddle Demo

編集:

CREATE OR ALTERを使用すると、2つのEXEC呼び出しを回避できます。

EXEC (N'CREATE OR ALTER PROCEDURE #PrintMax(@iInput NVARCHAR(MAX))
AS
BEGIN
    IF @iInput IS NULL
    RETURN;

    DECLARE @ReversedData NVARCHAR(MAX)
          , @LineBreakIndex INT
          , @SearchLength INT;

    SET @SearchLength = 4000;

    WHILE LEN(@iInput) > @SearchLength
    BEGIN
    SET @ReversedData = LEFT(@iInput COLLATE DATABASE_DEFAULT, @SearchLength);
    SET @ReversedData = REVERSE(@ReversedData COLLATE DATABASE_DEFAULT);
    SET @LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13), @ReversedData COLLATE DATABASE_DEFAULT);
    PRINT LEFT(@iInput, @SearchLength - @LineBreakIndex + 1);
    SET @iInput = RIGHT(@iInput, LEN(@iInput) - @SearchLength + @LineBreakIndex - 1);
    END;

    IF LEN(@iInput) > 0
    PRINT @iInput;
END;');

db <> fiddle Demo

1
Lukasz Szozda

ソースコードにLFがCRLFに置き換えられる問題がない場合、単純なコード出力に従うことでデバッグは不要です。

--http://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement
--Bill Bai
SET @SQL=replace(@SQL,char(10),char(13)+char(10))
SET @SQL=replace(@SQL,char(13)+char(13)+char(10),char(13)+char(10) )
DECLARE @Position int 
WHILE Len(@SQL)>0 
BEGIN
SET @Position=charindex(char(10),@SQL)
PRINT left(@SQL,@Position-2)
SET @SQL=substring(@SQL,@Position+1,len(@SQL))
end; 
0
Bill Bai

これは適切に機能するはずです。これは以前の回答の改善にすぎません。

DECLARE @Counter INT
DECLARE @Counter1 INT
SET @Counter = 0
SET @Counter1 = 0
DECLARE @TotalPrints INT
SET @TotalPrints = (LEN(@QUERY) / 4000) + 1
print @TotalPrints 
WHILE @Counter < @TotalPrints 
BEGIN
-- Do your printing...
print(substring(@query,@COUNTER1,@COUNTER1+4000))

set @COUNTER1 = @Counter1+4000
SET @Counter = @Counter + 1
END
0
vinbhai4u

別のバージョンがあります。これは、ループごとにメイン文字列を4000減らすことではなく、メイン文字列から印刷する各サブ文字列を抽出します(フードの下に非常に長い文字列がたくさん作成される可能性があります-不明)。

CREATE PROCEDURE [Internal].[LongPrint]
    @msg nvarchar(max)
AS
BEGIN

    -- SET NOCOUNT ON reduces network overhead
    SET NOCOUNT ON;

    DECLARE @MsgLen int;
    DECLARE @CurrLineStartIdx int = 1;
    DECLARE @CurrLineEndIdx int;
    DECLARE @CurrLineLen int;   
    DECLARE @SkipCount int;

    -- Normalise line end characters.
    SET @msg = REPLACE(@msg, char(13) + char(10), char(10));
    SET @msg = REPLACE(@msg, char(13), char(10));

    -- Store length of the normalised string.
    SET @MsgLen = LEN(@msg);        

    -- Special case: Empty string.
    IF @MsgLen = 0
    BEGIN
        PRINT '';
        RETURN;
    END

    -- Find the end of next substring to print.
    SET @CurrLineEndIdx = CHARINDEX(CHAR(10), @msg);
    IF @CurrLineEndIdx BETWEEN 1 AND 4000
    BEGIN
        SET @CurrLineEndIdx = @CurrLineEndIdx - 1
        SET @SkipCount = 2;
    END
    ELSE
    BEGIN
        SET @CurrLineEndIdx = 4000;
        SET @SkipCount = 1;
    END     

    -- Loop: Print current substring, identify next substring (a do-while pattern is preferable but TSQL doesn't have one).
    WHILE @CurrLineStartIdx < @MsgLen
    BEGIN
        -- Print substring.
        PRINT SUBSTRING(@msg, @CurrLineStartIdx, (@CurrLineEndIdx - @CurrLineStartIdx)+1);

        -- Move to start of next substring.
        SET @CurrLineStartIdx = @CurrLineEndIdx + @SkipCount;

        -- Find the end of next substring to print.
        SET @CurrLineEndIdx = CHARINDEX(CHAR(10), @msg, @CurrLineStartIdx);
        SET @CurrLineLen = @CurrLineEndIdx - @CurrLineStartIdx;

        -- Find bounds of next substring to print.              
        IF @CurrLineLen BETWEEN 1 AND 4000
        BEGIN
            SET @CurrLineEndIdx = @CurrLineEndIdx - 1
            SET @SkipCount = 2;
        END
        ELSE
        BEGIN
            SET @CurrLineEndIdx = @CurrLineStartIdx + 4000;
            SET @SkipCount = 1;
        END
    END
END
0
redcalx