web-dev-qa-db-it.com

Come stampare VARCHAR (MAX) usando Print Statement?

Ho un codice che è:

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)

La lunghezza dello script è di circa 10.000 caratteri e dal momento che sto usando la dichiarazione di stampa che può contenere solo un massimo di 8000. Quindi sto usando due dichiarazioni di stampa.

Il problema è quando ho uno script che è di dire 18000 caratteri, quindi ho usato 3 dichiarazioni di stampa.

Quindi c'è un modo per impostare il numero di istruzioni di stampa a seconda della lunghezza dello script?

78
peter

È possibile eseguire un ciclo WHILE in base al conteggio della lunghezza dello script diviso per 8000.

PER ESEMPIO:

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
14
Kelsey

So che è una vecchia domanda, ma quello che ho fatto non è menzionato qui.

Per me ha funzionato.

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)
163
alfoks

La seguente soluzione alternativa non utilizza l'istruzione PRINT. Funziona bene in combinazione con SQL Server Management Studio.

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

È possibile fare clic sul XML restituito per espanderlo nel visualizzatore XML incorporato.

C'è un limite lato client abbastanza generoso per le dimensioni visualizzate. Vai a Tools/Options/Query Results/SQL Server/Results to Grid/XML data per regolarlo se necessario.

69
Jirka Hanika

Ecco come dovrebbe essere fatto:

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, @[email protected], LEN(@String))   
END /*End While loop*/

Tratto da http://ask.sqlservercentral.com/questions/3102/any-way-around-the-print-limit-of-nvarcharmax-in-s.html

33
Ben B

È venuto attraverso questa domanda e voleva qualcosa di più semplice ... Prova la seguente:

SELECT [processing-instruction(x)][email protected] FOR XML PATH(''),TYPE
9
Edyn

Questo proc stampa correttamente il parametro VARCHAR(MAX) considerando il wrapping:

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
9
Andrey Morozov

Stavo cercando di utilizzare l'istruzione print per eseguire il debug di alcuni sql dinamici, come immagino che la maggior parte di voi stia utilizzando la stampa per motivi simili.

Ho provato alcune delle soluzioni elencate e ho trovato che la soluzione di Kelsey funziona con tweek minori (@sql è il mio @ script) n.b. LENGTH non è una funzione valida:

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

Questo codice fa come commentato aggiungere una nuova riga nell'output, ma per il debug questo non è un problema per me.

La soluzione di Ben B è perfetta ed è la più elegante, anche se per il debugging ci sono un sacco di righe di codice, quindi ho scelto di utilizzare la mia leggera modifica di Kelsey's. Potrebbe valere la pena creare un sistema come stored procedure in msdb per il codice di Ben B che potrebbe essere riutilizzato e chiamato in una riga?

Il codice di Alfoks non funziona purtroppo perché sarebbe stato più semplice.

7
Matthew Radford
 create la procedura dbo.PrintMax @text nvarchar (max) 
 as 
 begin 
 dichiarare @i int, @newline nchar (2), @print varchar (max); 
 set @newline = nchar (13) + nchar (10); 
 selezionare @i = charindex (@newline, @text); 
 while (@i> 0) 
 inizio
 selezionare @print = sottostringa (@ testo, 0, @ i); 
 while (len (@print)> 8000) 
 inizio
 stampa sottostringa (@ print, 0,8000); 
 selezionare @print = substring (@ print, 8000, len (@print)); 
 fine
 print @print; 
 selezionare @text = substring (@ text, @ i + 2, len (@text)); 
 selezionare @i = charindex (@newline, @text); 
 fine
 print @text; 
 end 
3
Adam Gering

Puoi usare questo

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

Utilizza i feed e gli spazi delle linee come un buon punto di interruzione:

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,@[email protected],1) <> ' ' and Substring(@sqlAll,@[email protected],1) <> @newline
    BEGIN
        set @nextspace = @nextspace + 1
    end
    print Substring(@sqlAll,@i,[email protected])
    set @i = @[email protected]
    set @nextspace = 0
end
print '   '
print '   '
print '   '
2
BickiBoy

C'è una grande funzione chiamata PrintMax scritta da Bennett Dill.

Ecco una versione leggermente modificata che utilizza la stored procedure temporanea per evitare la "polution dello schema" (idea da 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;');

Demo di DBFiddle

MODIFICARE:

Usando CREATE OR ALTER potremmo evitare due chiamate 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

Se il codice sorgente non presenterà problemi con LF da sostituire con CRLF, non è richiesto alcun debug seguendo i semplici output dei codici.

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

O semplicemente:

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

Questo dovrebbe funzionare correttamente questo è solo un miglioramento delle risposte precedenti.

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

Ecco un'altra versione. Questo estrae ciascuna sottostringa per stampare dalla stringa principale invece di ridurre la stringa principale di 4000 su ciascun ciclo (che potrebbe creare un sacco di stringhe molto lunghe sotto il cofano - non è sicuro).

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