Print Statement kullanarak VARCHAR (MAX) nasıl yazdırılır?


108

Bir kodum var:

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)

Komut Dosyasının uzunluğu yaklaşık 10.000 Karakterdir ve sadece maksimum 8000 tutabilen print Statement kullandığım için. Bu yüzden iki print cümlesi kullanıyorum.

Sorun, 18000 karakterlik bir betiğim olduğunda, daha sonra 3 yazdırma ifadesi kullanıyordum.

Öyleyse, komut dosyasının uzunluğuna bağlı olarak yazdırma ifadelerinin sayısını ayarlamanın bir yolu var mı?


1
Kullanmak zorunda mısınız PRINTveya diğer alternatiflere açık mısınız?
Martin Smith

Ben oluştururken (veya bulma ve oylama) üzerinde bir sorunla ilgili öneririm connect.microsoft.com/SQLServer/Feedback
jmoreno

Yanıtlar:


23

WHILEKomut dosyası uzunluğunuzun 8000'e bölünmesiyle bir döngü yapabilirsiniz .

ÖRNEĞİN:

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

Koduma bakarsanız, satır sonunu bulmak ve buna göre yazdırmak için @Pos değişkenini de kullanıyorum. Peki bunu kodunuzda nasıl kullanabilirim?
peter

@peter Akımı alıp SUBSTRsadece o anda uğraştığınız kısma bakabilir ve bunu yineleyebilirsiniz veya her seferinde 8k sınırından önce bir satır sonu olacağını biliyorsanız, o zaman sadece WHILEçizgiyi bulmaya dayalı olarak yapın molalar.
Kelsey

@peter Satır sonlarına göre döngü yapabilir misin? Örneğin satır sonu, satır sonundan sonraki 8k karaktere kadar baskı bulunursa, arama, yazdırma, yeni alt yapı vb.
Kelsey

1
İşlev LEN () değil UZUNLUK ()
shiggity

8
Senaryomu print(substring(@script, @Counter * 8000, (@Counter + 1) * 8000))yazdırırdım.
Lukas Thum

217

Bunun eski bir soru olduğunu biliyorum, ama benim yaptığım şey burada belirtilmedi.

Benim için aşağıdakiler çalıştı.

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)

4
@gordy - Bana öyle geliyor ki bu yöntem SSMS'de gerçekten çalışmıyor.
Jirka Hanika

1
Bu benim için CAST () veya CONVERT () kullanan SQL 2008 R2 SP2 (10.50.1600) ve SQL 2008 SP2 (10.0.5500) üzerinde çalışıyor.

26
16.002 karakterden sonra kesilme görüyorum, yine de daha uzun max. 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
ntext, text ve image veri türleri , Microsoft SQL Server'ın gelecekteki bir sürümünde kaldırılacaktır. Bu veri türlerini yeni geliştirme çalışmasında kullanmaktan kaçının ve şu anda bunları kullanan uygulamaları değiştirmeyi planlayın.
jumxozizi

5
Benim için SQL Server 2014 için SQL Server Management Studio'da çalışmadı. 16.000 karakterden sonra keser. Martin Smith tarafından yazıldığı gibi.
Jana Weschenfelder

103

Aşağıdaki geçici çözüm, PRINTifadeyi kullanmaz . SQL Server Management Studio ile birlikte iyi çalışır.

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

Yerleşik XML görüntüleyicide genişletmek için döndürülen XML'e tıklayabilirsiniz.

Görüntülenen boyutta oldukça cömert bir müşteri tarafı sınırı var. Tools/Options/Query Results/SQL Server/Results to Grid/XML dataGerekirse ayarlamak için gidin .


11
+1. Ancak bu yöntem, XML'de özel bir anlamı olan karakterleri kodlar. Örneğin, <ile değiştirilir &lt;.
Iain Samuel McLean Elder

5
Senaryoyu <root>....beğenmeden yazabilirsin :SELECT CAST(@MyLongString AS XML)
ali youhannaei

2
@aliyouhannaei - Evet ve hayır. Kök öğenin kesinlikle gerekli olmadığı konusunda haklısınız. Ancak, CDATA bölümü olmadan, yönteminiz bazı dizelerle sorun yaşamaya başlar. Özellikle <içerenler. XML değillerse, sorgu genellikle hata verir. XML iseler, dize başka bir "eşdeğer" XML biçiminde yeniden biçimlendirilebilir.
Jirka Hanika

8
@IainElder - Bu iyi bir nokta ve bunun için Adam Machanic'ten bir çözüm var . Bu da şu: SELECT @MyLongString AS [processing-instruction(x)] FOR XML PATH(''). Dize "x" adlı bir PI'ye sarılır, ancak PI başka bir öğeye (nedeniyle PATH('')) sarılmaz .
Jirka Hanika

Bu, "Alınan Maksimum Karakter - XML ​​verileri" sınırsız olarak ayarlanmış olsa bile çok uzun metinler için çalışmaz
Michael Møldrup

39

İşte bunun nasıl yapılması gerektiği:

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 adresinden alınmıştır


1
Harika teknik! BTW, bu tekniği ortaya çıkaran asıl makale SQLServerCentral.com'dan geldi >>> sqlservercentral.com/scripts/Print/63240
Rob.Kachmar

2
Bu benim için işe yaradı, ama aynı zamanda alan isimlerimden birini ikiye böldü. Öyleyse, bu yöntemi PRINT (@string) ve ardından EXECUTE (@string) için kullanırsam, EXECUTE başarısız olur.
Johnny Bones

1
PRINT işlevi kötü yerlerde satır sonları eklediğinden ve değerinden daha fazla temizlik gerektirdiğinden, bu benim için işe yaramıyor, ancak bu soruna en yakın çözüm.
Randy Burden

14

Bu soruyla karşılaştım ve daha basit bir şey istedim ... Aşağıdakileri deneyin:

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

5
Daha basit SELECT CAST(@STMT AS XML), başka bir yorumda belirtildiği gibi olacaktır . Tam olarak aynı çıktıyı üretir ve çıktı için saklı yordam oluşturmaktan daha az karmaşıktır.
Felix Bayer

4
@Felix Bu çok daha basit olsa da, SQL için pek işe yaramıyor. XML'e çevrim, SQL metnini XML'e dönüştürmeye çalışır. <,> Ve & lt ;, & gt; ve & amp; ve XML'de izin verilmeyen karakterleri işlemez. Ek olarak, <ve sonra> karşılaştırmasını yaptığınız bir durum varsa, bunun bir öğe olduğunu düşünür ve geçersiz bir düğüm hatası atar.
Edyn

12

Bu işlem, VARCHAR(MAX)sarmayı dikkate alarak parametreyi doğru şekilde yazdırır :

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

bu prosedürde Unicode karakterlerle çelişki vardır. örneğin utf8 nasıl kullanılır?
mostafa8026

Yukarıdaki yoruma verilen yanıtta, @script türü nvarchar olarak değiştirilerek yapılabilir.
mostafa8026

8

Çoğunuzun benzer nedenlerle print kullandığınızı düşündüğümden, bazı dinamik sql hatalarını ayıklamak için print deyimini kullanmak istiyordum.

Listelenen çözümlerden birkaçını denedim ve Kelsey'nin çözümünün küçük tweekslerle çalıştığını buldum (@sql benim @script'imdir) nb LENGTH geçerli bir işlev değil:

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

Bu kod, yorumlandığı gibi çıktıya yeni bir satır ekler, ancak hata ayıklama için bu benim için bir sorun değil.

Ben B'nin çözümü mükemmel ve en zarif olanıdır, ancak hata ayıklama için çok sayıda kod satırı vardır, bu yüzden Kelsey'in küçük modifikasyonunu kullanmayı seçiyorum. Ben B'nin kodu için bir satırda yeniden kullanılabilen ve çağrılabilen msdb'de saklı yordam gibi bir sistem oluşturmaya değer olabilir mi?

Alfoks'un kodu maalesef çalışmıyor çünkü bu daha kolay olurdu.


Ben B'nin çözümünü geçici saklı yordam olarak ekledim. Komut dosyalarımı biraz daha temiz tutuyor, ancak hata ayıklama için çok sayıda satır olduğuna katılıyorum.
Zarepheth

4

Bunu kullanabilirsin

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

Ben'in harika cevabından bir SP oluşturdum :

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

Harika, tam aradığım şey!
kooch

3
yordam oluşturma dbo.PrintMax @text nvarchar (max)
gibi
başla
    @i int, @newline nchar (2), @print varchar (maks); 
    @newline = nchar (13) + nchar (10) ayarla;
    @i = charindex (@newline, @text) seçin;
    süre (@i> 0)
    başla
        @print = alt dizeyi seçin (@ text, 0, @ i);
        while (len (@print)> 8000)
        başla
            baskı alt dizesi (@ baskı, 0,8000);
            @print = alt dizeyi seçin (@ print, 8000, len (@print));
        son
        print @print;
        @metin = alt dizeyi seçin (@ metin, @ i + 2, len (@text));
        @i = charindex (@newline, @text) seçin;
    son
    print @text;
son

2

Bennett Dill tarafından yazılan PrintMax adlı harika bir işlev var .

Burada, "şema kirliliğinden" kaçınmak için geçici saklı yordamı kullanan biraz değiştirilmiş sürüm ( https://github.com/Toolien/sp_GenMerge/blob/master/sp_GenMerge.sql'den fikir )

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

DÜZENLE:

Kullanarak CREATE OR ALTERiki EXEC çağrısını önleyebiliriz:

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 Demosu


2

Satır Beslemelerini ve boşlukları iyi bir kırılma noktası olarak kullanır:

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

Kusursuz bir şekilde çalıştı
Jolley71717

2

Ya da sadece:

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

0

İşte başka bir versiyon. Bu, ana dizeyi her döngüde 4000 azaltmak yerine yazdırmak için her bir alt dizeyi ana dizeden çıkarır (bu, başlık altında çok uzun dizeler oluşturabilir - emin değilim).

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

Bu düzgün çalışmalıdır, bu sadece önceki cevapların bir gelişmesidir.

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

Kaynak kodun CRLF ile değiştirilecek LF ile ilgili sorunları olmayacaksa, basit kod çıktılarını izleyerek hata ayıklama gerekmez.

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

Çıktıda bozuk satır kesmelerini önlemek için PrintMax sürümüm:


    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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.