web-dev-qa-db-de.com

Wie drucke ich VARCHAR (MAX) mit Print Statement?

Ich habe einen Code, der lautet:

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)

Die Länge des Skripts beträgt etwa 10.000 Zeichen. Seitdem verwende ich die print-Anweisung, die maximal 8000 aufnehmen kann. Ich verwende also zwei Druckanweisungen.

Das Problem ist, wenn ich ein Skript mit etwa 18000 Zeichen habe, dann habe ich 3 Druckanweisungen verwendet.

Gibt es eine Möglichkeit, die Anzahl der Druckanweisungen abhängig von der Länge des Skripts festzulegen?

78
peter

Sie können eine WHILE-Schleife basierend auf der Zählung Ihrer Skriptlänge durch 8000 durchführen.

Z.B:

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

Ich weiß, dass es eine alte Frage ist, aber was ich getan habe, wird hier nicht erwähnt.

Für mich hat das folgende funktioniert.

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)
163
alfoks

Die folgende Problemumgehung verwendet die PRINT-Anweisung nicht. Es funktioniert gut in Kombination mit dem SQL Server Management Studio.

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

Sie können auf das zurückgegebene XML klicken, um es im integrierten XML-Viewer zu erweitern.

Es gibt ein ziemlich großzügiges clientseitiges Limit für die angezeigte Größe. Gehen Sie zu Tools/Options/Query Results/SQL Server/Results to Grid/XML data, um ihn ggf. anzupassen.

69
Jirka Hanika

So soll das gemacht werden:

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

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

33
Ben B

Kam über diese Frage und wollte etwas Einfacheres ... Versuchen Sie folgendes:

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

Diese Prozedur gibt VARCHAR(MAX)-Parameter unter Berücksichtigung des Umlaufs korrekt aus:

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

Ich habe versucht, die print-Anweisung zum Debuggen von dynamischem SQL zu verwenden, da ich mir vorstelle, dass die meisten von Ihnen print aus ähnlichen Gründen verwenden.

Ich habe einige der aufgelisteten Lösungen ausprobiert und festgestellt, dass die Lösung von Kelsey mit kleineren Tweets funktioniert (@sql ist mein @script) n.b. LENGTH ist keine gültige Funktion:

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

Dieser Code fügt zwar kommentiert eine neue Zeile in die Ausgabe ein, aber für das Debuggen ist dies kein Problem für mich.

Die Lösung von Ben B ist perfekt und ist die eleganteste, obwohl für das Debuggen eine Menge Codezeilen zur Verfügung stehen, also wähle ich meine leichte Modifikation von Kelsey. Es könnte sich lohnen, ein System wie die gespeicherte Prozedur in msdb für den Code von Ben B zu erstellen, das wiederverwendet und in einer Zeile aufgerufen werden kann.

Alfoks 'Code funktioniert leider nicht, weil das einfacher gewesen wäre.

7
Matthew Radford
 Erstellen Sie die Prozedur dbo.PrintMax @text nvarchar (max) 
 als 
 begin 
 @ i int deklarieren, @newline nchar (2), @print varchar (max); 
 set @newline = nchar (13) + nchar (10); 
 Wählen Sie @i = charindex (@newline, @text); 
 while (@i> 0) 
 Start
 select @print = Teilstring (@ text, 0, @ i); 
 while (len (@print)> 8000) 
 Start
 Teilstring drucken (@ print, 0,8000); 
 select @print = Teilstring (@ print, 8000, len (@print)); 
 Ende
 print @print; 
 select @text = substring (@ text, @ i + 2, len (@text)); 
 Wählen Sie @i = charindex (@newline, @text); 
 Ende
 print @text; 
 end 
3
Adam Gering

Sie können dies verwenden

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

Verwendet Zeilenvorschübe und Leerzeichen als einen guten Haltepunkt:

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

Es gibt eine großartige Funktion namens PRINTMAX VON BENNETT DILL.

Hier ist eine leicht modifizierte Version, die eine temporäre gespeicherte Prozedur verwendet, um "Schema-Verschmutzung" zu vermeiden (Idee von 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

EDIT:

Mit CREATE OR ALTER könnten wir zwei EXEC-Aufrufe vermeiden:

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

1
Lukasz Szozda

Wenn der Quellcode keine Probleme mit LF hat, die durch CRLF ersetzt werden sollen, ist für die Ausgabe einfacher Codes kein Debugging erforderlich.

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

Oder einfach:

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

Dies sollte richtig funktionieren, dies ist nur eine Verbesserung der bisherigen Antworten.

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

Hier ist eine andere Version. Hier werden die zu druckenden Teilstrings aus dem Hauptstring extrahiert, anstatt den Hauptstring in jeder Schleife um 4000 zu reduzieren (was sehr viele lange Strings unter der Motorhaube erzeugen kann - nicht sicher).

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