Wie drucke ich VARCHAR (MAX) mit Print Statement?

108

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 ungefähr 10.000 Zeichen. Da ich eine Druckanweisung verwende, die nur maximal 8000 Zeichen enthalten kann, verwende ich zwei Druckanweisungen.

Das Problem ist, wenn ich ein Skript mit beispielsweise 18000 Zeichen habe, habe ich 3 print-Anweisungen verwendet.

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

Peter
quelle
1
Müssen Sie PRINTandere Alternativen nutzen oder sind Sie offen dafür?
Martin Smith
Ich würde vorschlagen, ein Problem auf connect.microsoft.com/SQLServer/Feedback
jmoreno

Antworten:

23

Sie können eine WHILESchleife basierend auf der Anzahl Ihrer Skriptlängen geteilt durch 8000 erstellen.

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
Kelsey
quelle
Wenn Sie sich meinen Code ansehen, verwende ich auch die Variable @Pos, um den Zeilenumbruch zu finden und entsprechend zu drucken. Wie könnte ich das in Ihrem Code verwenden?
Peter
@peter Sie können einfach den aktuellen Wert nehmen SUBSTRund nur den Teil betrachten, mit dem Sie sich gerade befassen, und diesen wiederholen. Wenn Sie wissen, dass jedes Mal vor dem 8-KB-Grenzwert ein Zeilenumbruch auftritt, führen Sie einfach die WHILEauf der Suche basierende Zeile aus geht kaputt.
Kelsey
@ Peter Kannst du eine Schleife basierend auf den Zeilenumbrüchen machen? Suchen Sie beispielsweise nach Zeilenumbruch, wenn gefunden, bis Zeilenumbruch drucken, Teil vom Zeilenumbruch bis zu den nächsten 8.000 Zeichen, suchen, drucken, neuer Teil usw.?
Kelsey
1
Die Funktion ist LEN () nicht LENGTH ()
Shiggity
8
Ich habe print(substring(@script, @Counter * 8000, (@Counter + 1) * 8000))mein Skript gedruckt.
Lukas Thum
217

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

Für mich hat folgendes funktioniert.

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)
Alfoks
quelle
4
@gordy - Es scheint mir also, dass diese Methode in SSMS nicht wirklich funktioniert.
Jirka Hanika
1
Dies funktioniert für mich unter SQL 2008 R2 SP2 (10.50.1600) mit CAST () oder CONVERT () und unter SQL 2008 SP2 (10.0.5500).
25
Ich sehe Kürzungen nach 16.002 Zeichen, immer noch länger als maxobwohl. DECLARE @info NVARCHAR(MAX) = 'A';SET @info = REPLICATE(@info, 16000) + 'BC This is not printed';PRINT @info;PRINT CAST(@info AS NTEXT);
Martin Smith
6
Die Datentypen ntext, text und image werden in einer zukünftigen Version von Microsoft SQL Server entfernt. Vermeiden Sie die Verwendung dieser Datentypen in neuen Entwicklungsarbeiten und planen Sie, Anwendungen zu ändern, die sie derzeit verwenden.
Jumxozizi
5
Hat bei mir in SQL Server Management Studio für SQL Server 2014 nicht funktioniert. Es schneidet nach 16.000 Zeichen. Wie von Martin Smith geschrieben.
Jana Weschenfelder
103

Die folgende Problemumgehung verwendet die PRINTAnweisung nicht. Es funktioniert gut in Kombination mit 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 eine ziemlich großzügige clientseitige Begrenzung für die angezeigte Größe. Gehen Sie zu Tools/Options/Query Results/SQL Server/Results to Grid/XML data, um es bei Bedarf anzupassen.

Jirka Hanika
quelle
11
+1. Diese Methode codiert jedoch Zeichen, die in XML eine besondere Bedeutung haben. Zum Beispiel <wird durch ersetzt &lt;.
Iain Samuel McLean Elder
5
Sie können Skript schreiben ohne <root>....:SELECT CAST(@MyLongString AS XML)
Ali Youhannaei
2
@aliyouhannaei - Ja und nein. Sie haben Recht, dass das Stammelement nicht unbedingt erforderlich ist. Ohne den CDATA-Abschnitt hat Ihre Methode jedoch Probleme mit einigen Zeichenfolgen. Besonders diejenigen, die <enthalten. Wenn es sich nicht um XML handelt, wird die Abfrage normalerweise fehlerhaft ausgeführt. Wenn es sich um XML handelt, wird die Zeichenfolge möglicherweise in eine andere "äquivalente" XML-Form umformatiert.
Jirka Hanika
8
@IainElder - Das ist ein guter Punkt und es gibt eine Problemumgehung dafür von Adam Machanic . Es ist das : SELECT @MyLongString AS [processing-instruction(x)] FOR XML PATH(''). Die Zeichenfolge wird in einen PI mit dem Namen "x" eingeschlossen, aber der PI wird (wegen PATH('')) nicht in ein anderes Element eingeschlossen .
Jirka Hanika
Dies funktioniert nicht für sehr lange Texte, selbst wenn "Maximum Characters Retrieved - XML ​​data" auf "Unlimited" gesetzt ist
Michael Møldrup
39

So sollte 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, @CurrentEnd+@offset, LEN(@String))   
END /*End While loop*/

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

Ben B.
quelle
1
Tolle Technik! Übrigens war der eigentliche Artikel, der diese Technik hervorgebracht hat, von SQLServerCentral.com >>> sqlservercentral.com/scripts/Print/63240
Rob.Kachmar
2
Das hat bei mir funktioniert, aber es hat auch einen meiner Feldnamen in zwei Hälften geteilt. Wenn ich diese Methode zum DRUCKEN (@string) und dann zum EXECUTE (@string) verwendet habe, schlägt das EXECUTE fehl.
Johnny Bones
1
Dies funktioniert bei mir nicht, da die PRINT-Funktion an schlechten Stellen Zeilenumbrüche hinzufügt und mehr Bereinigung erfordert, als es wert ist, aber dies ist die am nächsten liegende Lösung für das Problem.
Randy Burden
14

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

SELECT [processing-instruction(x)]=@Script FOR XML PATH(''),TYPE
Edyn
quelle
5
Einfacher wäre, SELECT CAST(@STMT AS XML)wie bereits in einem anderen Kommentar erwähnt. Erzeugt genau die gleiche Ausgabe und ist in der Tat weniger kompliziert als das Erstellen einer gespeicherten Prozedur für die Ausgabe.
Felix Bayer
4
@Felix Das wäre zwar viel einfacher, funktioniert aber für SQL nicht ganz. Beim Casting in XML wird versucht, den SQL-Text in XML zu konvertieren. Es ersetzt <,> und & durch & lt;, & gt; und & amp; Zeichen, die in XML nicht zulässig sind, werden nicht verarbeitet. Wenn Sie eine Situation haben, in der Sie <und dann> vergleichen, wird dies als Element angesehen und ein ungültiger Knotenfehler ausgelöst.
Edyn
12

Dieser Prozess druckt den VARCHAR(MAX)Parameter unter Berücksichtigung des Umbringens 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
Andrey Morozov
quelle
Diese Prozedur hat einen Konflikt mit Unicode-Zeichen. Wie gehe ich zum Beispiel mit utf8 um?
mostafa8026
In der Antwort auf den obigen Kommentar kann dies durch Ändern des @ script-Typs in nvarchar erfolgen.
mostafa8026
8

Ich wollte die print-Anweisung verwenden, um einige dynamische SQL-Dateien zu debuggen, 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 Kelseys Lösung mit kleinen Wochen funktioniert (@sql ist mein @script). Nb 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 wie kommentiert eine neue Zeile in die Ausgabe ein, aber für das Debuggen ist dies für mich kein Problem.

Die Lösung von Ben B ist perfekt und die eleganteste, obwohl das Debuggen viele Codezeilen umfasst, sodass ich meine geringfügige Modifikation von Kelsey verwende. Es könnte sich lohnen, ein System wie eine gespeicherte Prozedur in msdb für den Code von Ben B zu erstellen, das in einer Zeile wiederverwendet und aufgerufen werden kann.

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

Matthew Radford
quelle
Ich habe gerade die Lösung von Ben B als temporär gespeicherte Prozedur hinzugefügt. Hält meine Skripte ein bisschen sauberer, aber ich stimme zu, dass es viele Zeilen zum Debuggen gibt.
Zarepheth
4

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
Marwan Almukh
quelle
4

Ich habe gerade einen SP aus Bens großartiger Antwort erstellt :

/*
---------------------------------------------------------------------------------
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
Yovav
quelle
Wunderbar, genau das, wonach ich gesucht habe!
Kooch
3
Prozedur erstellen dbo.PrintMax @text nvarchar (max)
wie
Start
    deklarieren Sie @i int, @newline nchar (2), @print varchar (max); 
    setze @newline = nchar (13) + nchar (10);
    Wählen Sie @i = charindex (@newline, @text);
    während (@i> 0)
    Start
        Wählen Sie @print = Teilzeichenfolge (@ text, 0, @ i).
        while (len (@print)> 8000)
        Start
            Teilzeichenfolge drucken (@ print, 0,8000);
            Wählen Sie @print = Teilzeichenfolge (@ print, 8000, len (@print)).
        Ende
        print @print;
        wähle @text = Teilzeichenfolge (@ text, @ i + 2, len (@text));
        Wählen Sie @i = charindex (@newline, @text);
    Ende
    print @text;
Ende
Adam Gering
quelle
2

Es gibt eine großartige Funktion namens PrintMax, die von Bennett Dill geschrieben wurde .

Hier ist eine leicht modifizierte Version, die temporäre gespeicherte Prozeduren 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

BEARBEITEN:

Mit CREATE OR ALTERkö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 <> fiddle Demo

Lukasz Szozda
quelle
2

Verwendet Zeilenvorschübe und Leerzeichen als 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,@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 '   '
BickiBoy
quelle
Funktionierte einwandfrei
Jolley71717
2

Oder einfach:

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

Hier ist eine andere Version. Dieser extrahiert jeden zu druckenden Teilstring aus der Hauptzeichenfolge, anstatt die Hauptzeichenfolge in jeder Schleife um 4000 zu reduzieren (was unter der Haube viele sehr lange Zeichenfolgen 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
Redcalx
quelle
0

Dies sollte ordnungsgemäß funktionieren. Dies ist nur eine Verbesserung der vorherigen 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
vinbhai4u
quelle
0

Wenn der Quellcode keine Probleme mit LF hat, die durch CRLF ersetzt werden sollen, ist kein Debuggen erforderlich, indem einfache Code-Ausgaben befolgt werden.

--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; 
Bill Bai
quelle
0

Meine PrintMax-Version zur Vermeidung fehlerhafter Zeilenumbrüche bei der Ausgabe:


    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
Ercument Eskar
quelle