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


108

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

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つの印刷ステートメントを使用していたことです。

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


1
PRINT他の選択肢を使用する必要がありますか、それとも利用できますか?
マーティン・スミス

回答:


23

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

例えば:

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

私のコードを見ると、@ Pos変数を使用して改行を見つけ、それに応じて印刷しています。それで、コードでそれをどのように使用できますか?
ピーター、2011年

@peter電流SUBSTRを取得して、その時点で処理している部分だけを見て繰り返し、または毎回8k制限の前に改行があることがわかっている場合は、ラインのWHILE検出に基づいて壊れる。
ケルシー

@peterは改行に基づいてループできますか?たとえば、改行を探します。見つかった場合、改行までの印刷、改行から次の8k文字までのsubstr、検索、印刷、新しいsubstrなど?
ケルシー

1
関数はLENGTH()ではなくLEN()です
shiggity

8
以前print(substring(@script, @Counter * 8000, (@Counter + 1) * 8000))はスクリプトを印刷していました。
Lukas Thum、2018年

216

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

私にとっては次のことがうまくいきました。

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)

4
@gordy-したがって、この方法はSSMSでは実際には機能しないようです。
Jirka Hanika 2013

1
これは、CAST()またはCONVERT()を使用するSQL 2008 R2 SP2(10.50.1600)、およびSQL 2008 SP2(10.0.5500)で私にとっては機能します。

26
16,002文字を超えると切り捨てられますが、それでもまだ長くなってmaxいます。DECLARE @info NVARCHAR(MAX) = 'A';SET @info = REPLICATE(@info, 16000) + 'BC This is not printed';PRINT @info;PRINT CAST(@info AS NTEXT);
マーティン・スミス

6
ntext、text、およびimageデータ型は、Microsoft SQL Serverの将来のバージョンで削除される予定です。新しい開発作業でこれらのデータ型を使用することは避け、現在それらを使用しているアプリケーションの変更を計画してください。
jumxozizi

5
SQL Server 2014のSQL Server Management Studioでは機能しませんでした。16,000文字を超えると切り捨てられます。マーティン・スミスによって書かれた。
Jana Weschenfelder、

103

次の回避策では、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必要に応じて、調整してください。


11
+1。ただし、このメソッドはXMLで特別な意味を持つ文字をエンコードします。たとえば、<はに置き換えられ&lt;ます。
Iain Samuel McLean Elder

5
あなたはせずにスクリプトを書くことができます<root>....ように:SELECT CAST(@MyLongString AS XML)
アリyouhannaei

2
@aliyouhannaei-はい、いいえ。ルート要素は必ずしも必要ではないというのはあなたの言うとおりです。ただし、CDATAセクションがないと、メソッドで一部の文字列に問題が発生し始めます。特に<を含むもの。XMLでない場合、クエリは通常エラーになります。XMLの場合、文字列は最終的に別の「同等の」XML形式に再フォーマットされる可能性があります。
Jirka Hanika、2015

8
@IainElder-これは良い点であり、Adam Machanicによる回避策があります。これです:SELECT @MyLongString AS [processing-instruction(x)] FOR XML PATH('')。文字列は "x"と呼ばれるPIでラップされますが、PIは別の要素でラップされません(ためPATH(''))。
Jirka Hanika、2015

「取得した最大
文字数-XML

39

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

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から取得


1
素晴らしいテクニック!ところで、この手法を生み出した実際の記事は、SQLServerCentral.comからのものです>>> sqlservercentral.com/scripts/Print/63240
Rob.Kachmar

2
これは私にとってはうまくいきましたが、私のフィールド名の1つを半分に切り落としました。したがって、このメソッドを使用してPRINT(@string)を実行し、次にEXECUTE(@string)を実行すると、EXECUTEは失敗します。
Johnny Bones 2016年

1
PRINT関数は悪い場所に改行を追加し、それ以上のクリーンアップが必要になるため、これは私には機能しませんが、これが問題に対する最も近い解決策です。
Randy Burden

14

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

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

5
より簡単なのはSELECT CAST(@STMT AS XML)、別のコメントですでに述べたとおりです。まったく同じ出力を生成し、出力用のストアドプロシージャを作成するよりも実際には複雑ではありません。
Felix Bayer

4
@Felixこれははるかに単純ですが、SQLではうまく機能しません。XMLにキャストすると、SQLテキストがXMLに変換されます。<、>、&を&lt;、&gt;に置き換えます。および&amp; また、XMLで許可されていない文字は処理しません。さらに、<を比較してから>を比較する状況がある場合、それは要素であると見なされ、無効ノードエラーがスローされます。
Edyn

12

このプロシージャは、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

この手順はUnicode文字と競合します。たとえば、utf8の処理方法は?
mostafa8026

上記のコメントへの返信では、@ scriptタイプをnvarcharに変更することで実行できます。
mostafa8026

8

多くの人が同様の理由でprintを使用していると想像して、printステートメントを使用して動的SQLをデバッグしようとしていました。

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

--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のソリューションは完璧で最も優雅ですが、デバッグにはコードの多くの行があるため、ケルシーのわずかな変更を使用することを選択します。ベンドBのコード用にmsdbでストアドプロシージャのようなシステムを作成して、再利用して1行で呼び出すことができるようにするとよいでしょう。

残念ながら、Alfoksのコードは機能しません。


一時的なストアドプロシージャとしてBen Bのソリューションを追加しました。スクリプトを少しクリーンに保ちますが、デバッグの行が多いことに同意します。
ザレフェス2017

4

これを使えます

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

4

私はベンの素晴らしい答えからSPを作成しました:

/*
---------------------------------------------------------------------------------
PURPOSE   : Print a string without the limitation of 4000 or 8000 characters.
/programming/7850477/how-to-print-varcharmax-using-print-statement
USAGE     : 
DECLARE @Result NVARCHAR(MAX)
SET @Result = 'TEST'
EXEC [dbo].[Print_Unlimited] @Result
---------------------------------------------------------------------------------
*/
ALTER PROCEDURE [dbo].[Print_Unlimited]
    @String NVARCHAR(MAX)
AS

BEGIN

    BEGIN TRY
    ---------------------------------------------------------------------------------

    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*/

    ---------------------------------------------------------------------------------
    END TRY
    BEGIN CATCH
        DECLARE @ErrorMessage VARCHAR(4000)
        SELECT @ErrorMessage = ERROR_MESSAGE()    
        RAISERROR(@ErrorMessage,16,1)
    END CATCH
END

素晴らしい、まさに私が探していたもの!
kooch

3
作成プロシージャdbo.PrintMax @text nvarchar(max)
なので
ベギン
    @i int、@ newline nchar(2)、@ print varchar(max)を宣言します。 
    set @newline = nchar(13)+ nchar(10);
    @i = charindex(@newline、@text);を選択します。
    while(@i> 0)
    ベギン
        @print = substring(@ text、0、@ i);を選択します。
        while(len(@print)> 8000)
        ベギン
            substring(@ print、0,8000);を出力します。
            select @print = substring(@ print、8000、len(@print));
        終わり
        @printを印刷します。
        select @text = substring(@ text、@ i + 2、len(@text));
        @i = charindex(@newline、@text);を選択します。
    終わり
    @textを印刷します。
終わり

2

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デモ

編集:

これ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デモ


2

改行としてスペースと改行を使用します。

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 '   '

完璧に働きました
Jolley71717 2017年

2

または単に:

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

0

ここに別のバージョンがあります。これは各ループでメイン文字列を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

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

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

ソースコードに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

出力の不良改行を防ぐための私のPrintMaxバージョン:


    CREATE PROCEDURE [dbo].[PrintMax](@iInput NVARCHAR(MAX))
    AS
    BEGIN
      Declare @i int;
      Declare @NEWLINE char(1) = CHAR(13) + CHAR(10);
      While LEN(@iInput)>0 BEGIN
        Set @i = CHARINDEX(@NEWLINE, @iInput)
        if @i>8000 OR @i=0 Set @i=8000
        Print SUBSTRING(@iInput, 0, @i)
        Set @iInput = SUBSTRING(@iInput, @i+1, LEN(@iInput))
      END
    END
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.