Как распечатать VARCHAR (MAX)с помощью инструкции Print?


У меня есть код, который выглядит так:

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 символов, и поскольку я использую оператор print, который может содержать только максимум 8000. Поэтому я использую два оператора печати.

проблема в том, что когда у меня есть скрипт, который имеет, скажем, 18000 символов, то я использовал 3 Инструкции печати.

Так есть ли способ, которым я мог бы установить количество операторов печати в зависимости от длины скрипта?

13 69

13 ответов:

вы могли бы сделать 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

Я знаю, это старый вопрос, но то, что я не упомянул здесь.

для меня сработало следующее.

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)

следующий обходной путь не использует 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 чтобы настроить его, если это необходимо.

вот как это должно быть сделано:

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

наткнулся на этот вопрос и хотел что-то более простое... Попробуйте следующее:

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

Я хотел использовать инструкцию print для отладки некоторого динамического sql, поскольку я предполагаю, что большинство из вас использует print по аналогичным причинам.

я попробовал несколько из перечисленных решений и обнаружил, что решение Келси работает с незначительными твиками (@sql-это мой @script) n.B. длина не является допустимой функцией:

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

этот код не прокомментировал добавить новую строку в выходной, но для отладки это не проблема для меня.

решение Бена Б идеально и является большинство elegent, хотя для отладки много строк кода, поэтому я решил использовать свою небольшую модификацию Kelsey's. возможно, стоит создать систему, подобную хранимой процедуре в msdb для кода Ben B, который можно повторно использовать и вызывать в одной строке?

код Alfoks не работает, к сожалению, потому что это было бы проще.

этот процесс правильно печатает

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

вы можете использовать этот

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

использует линейные каналы и пробелы в качестве хорошей точки останова:

объявить @sqlAll как nvarchar(Макс) установите @sqlAll = ' -- вставьте все ваши sql здесь

' печать '@sqlAll-усечено более 4000' print @sqlAll печать ' ' печать ' ' печать ' '

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

есть отличная функция под названием PrintMax написано Беннетт Дилл.

вот немного измененная версия, которая использует хранимую процедуру temp, чтобы избежать "загрязнения схемы" (идея от 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

EDIT:

используя CREATE OR ALTER мы могли бы избежать двух 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

вот еще одна версия. Этот извлекает каждую подстроку для печати из основной строки вместо того, чтобы уменьшать основную строку на 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

Это должно работать правильно это просто улучшение предыдущих ответов.

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