Làm cách nào để in VARCHAR (MAX) bằng Print Statement?


108

Tôi có một mã là:

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)

Độ dài của Tập lệnh là khoảng 10.000 Ký tự và Vì tôi đang sử dụng Câu lệnh in chỉ có thể chứa tối đa 8000. Vì vậy, tôi đang sử dụng hai câu lệnh in.

Vấn đề là khi tôi có một tập lệnh có 18000 ký tự thì tôi đã sử dụng 3 câu lệnh in.

Vì vậy, có cách nào mà tôi có thể đặt số lượng câu lệnh in tùy thuộc vào độ dài của tập lệnh không?


1
Bạn có phải sử dụng PRINThoặc bạn có sẵn sàng cho các lựa chọn thay thế khác?
Martin Smith

Tôi khuyên bạn nên tạo (hoặc tìm và bỏ phiếu) cho một vấn đề trên connect.microsoft.com/SQLServer/Feedback
jmoreno

Câu trả lời:


23

Bạn có thể thực hiện một WHILEvòng lặp dựa trên số lượng chiều dài tập lệnh của bạn chia cho 8000.

VÍ DỤ:

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

Nếu bạn nhìn vào mã của tôi, tôi cũng đang sử dụng biến @Pos để tìm ngắt dòng và in cho phù hợp. Vì vậy, làm thế nào tôi có thể sử dụng điều đó trong mã của bạn.
peter

@peter Bạn chỉ có thể lấy hiện tại SUBSTRvà chỉ xem phần bạn đang xử lý tại thời điểm đó và lặp lại phần đó hoặc nếu bạn biết rằng sẽ có ngắt dòng trước giới hạn 8k mỗi lần thì chỉ cần thực hiện WHILEdựa trên tìm dòng nghỉ giải lao.
Kelsey

@peter bạn có thể lặp lại dựa trên các ngắt dòng không? Ví dụ: tìm dấu ngắt dòng, nếu tìm thấy in đến ngắt dòng, dấu phụ từ ngắt dòng đến ký tự 8k tiếp theo, tìm kiếm, in, dấu chấm mới, v.v.?
Kelsey

1
Chức năng là LEN () không LENGTH ()
shiggity

8
Tôi đã từng print(substring(@script, @Counter * 8000, (@Counter + 1) * 8000))in kịch bản của mình.
Lukas Thum

217

Tôi biết đó là một câu hỏi cũ, nhưng những gì tôi đã làm không được đề cập ở đây.

Đối với tôi những điều sau đây đã hoạt động.

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)

4
@gordy - Đối với tôi, có vẻ như phương pháp này không thực sự hoạt động trong SSMS.
Jirka Hanika

1
Điều này phù hợp với tôi trên SQL 2008 R2 SP2 (10.50.1600) sử dụng CAST () hoặc CONVERT () và trên SQL 2008 SP2 (10.0.5500).

26
Tôi thấy phần bị cắt sau 16.002 ký tự, vẫn dài hơn 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
Các kiểu dữ liệu ntext, văn bản và hình ảnh sẽ bị xóa trong phiên bản Microsoft SQL Server trong tương lai. Tránh sử dụng các loại dữ liệu này trong công việc phát triển mới và lên kế hoạch sửa đổi các ứng dụng hiện đang sử dụng chúng.
jumxozizi

5
Không hoạt động đối với tôi trong SQL Server Management Studio dành cho SQL Server 2014. Nó cắt sau 16.000 ký tự. Như được viết bởi Martin Smith.
Jana Weschenfelder

103

Cách giải quyết sau không sử dụng PRINTcâu lệnh. Nó hoạt động tốt khi kết hợp với SQL Server Management Studio.

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

Bạn có thể nhấp vào XML được trả về để mở rộng nó trong trình xem XML tích hợp sẵn.

Có một giới hạn phía khách hàng khá hào phóng về kích thước hiển thị. Đi tới Tools/Options/Query Results/SQL Server/Results to Grid/XML datađể điều chỉnh nó nếu cần.


11
+1. Nhưng phương pháp này mã hóa các ký tự có ý nghĩa đặc biệt trong XML. Ví dụ, <được thay thế bằng &lt;.
Iain Samuel McLean Elder,

5
bạn có thể viết kịch bản mà không cần <root>....như:SELECT CAST(@MyLongString AS XML)
ali youhannaei

2
@aliyouhannaei - Có và không. Bạn nói đúng rằng phần tử gốc không hoàn toàn cần thiết. Tuy nhiên, nếu không có phần CDATA, phương pháp của bạn bắt đầu gặp sự cố với một số chuỗi. Đặc biệt là những cái có chứa <. Nếu chúng không phải là XML, truy vấn thường sẽ xảy ra lỗi. Nếu chúng là XML, chuỗi có thể được định dạng lại thành một dạng XML "tương đương" khác.
Jirka Hanika

8
@IainElder - Đó là một điểm tốt và có một giải pháp cho nó từ Adam Machanic . Đó là điều này: SELECT @MyLongString AS [processing-instruction(x)] FOR XML PATH(''). Chuỗi sẽ được bọc trong một PI được gọi là "x", nhưng PI sẽ không được bọc trong một phần tử khác (vì PATH('')).
Jirka Hanika

Điều này sẽ không làm việc cho văn bản rất dài, ngay cả với "nhân vật tối đa Lấy - dữ liệu XML" thiết lập để không giới hạn
Michael Møldrup

39

Đây là cách điều này nên được thực hiện:

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

Lấy từ http://ask.sqlservercentral.com/questions/3102/any-way-around-the-print-limit-of-nvarcharmax-in-s.html


1
Kỹ thuật tuyệt vời! BTW, bài viết thực tế mà có nguồn gốc từ kỹ thuật này là từ SQLServerCentral.com >>> sqlservercentral.com/scripts/Print/63240
Rob.Kachmar

2
Điều này có hiệu quả với tôi, nhưng nó cũng cắt một nửa tên trường của tôi. Vì vậy, nếu tôi sử dụng phương pháp này để IN (@string) và sau đó EXECUTE (@string), thì EXECUTE không thành công.
Johnny Bones

1
Điều này không hiệu quả với tôi vì chức năng PRINT thêm các ngắt dòng ở những vị trí xấu và sẽ yêu cầu dọn dẹp nhiều hơn mức đáng có nhưng đây là giải pháp gần nhất cho vấn đề.
Randy Burden,

14

Gặp phải câu hỏi này và muốn một cái gì đó đơn giản hơn ... Hãy thử những điều sau:

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

5
Đơn giản hơn sẽ được SELECT CAST(@STMT AS XML)nêu trong một bình luận khác. Tạo ra chính xác cùng một đầu ra và thực sự ít phức tạp hơn so với việc tạo ra một quy trình lưu trữ cho đầu ra.
Felix Bayer

4
@Felix Mặc dù điều đó sẽ đơn giản hơn nhiều nhưng nó không hoàn toàn hoạt động đối với SQL. Truyền sang XML sẽ cố gắng chuyển đổi văn bản SQL sang XML. Nó sẽ thay thế <,> và & bằng & lt ;, & gt; và & amp; và nó sẽ không xử lý các ký tự không được phép trong XML. Ngoài ra, nếu bạn gặp trường hợp so sánh <và sau đó>, nó cho rằng đó là một phần tử và tạo ra lỗi nút không hợp lệ.
Edyn

12

Proc này in ra VARCHAR(MAX)thông số một cách chính xác khi xem xét gói:

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

thủ tục này có xung đột với các ký tự Unicode. làm thế nào để xử lý utf8 chẳng hạn?
mostafa8026

trong câu trả lời cho nhận xét trên, bạn có thể thực hiện bằng cách thay đổi kiểu @script thành nvarchar.
mostafa8026

8

Tôi đang tìm cách sử dụng câu lệnh print để gỡ lỗi một số sql động như tôi tưởng tượng hầu hết các bạn đang sử dụng print vì lý do simliar.

Tôi đã thử một số giải pháp được liệt kê và nhận thấy rằng giải pháp của Kelsey hoạt động với các tweeks nhỏ (@sql là @script của tôi) nb LENGTH không phải là một hàm hợp lệ:

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

Mã này như đã nhận xét thêm một dòng mới vào đầu ra, nhưng để gỡ lỗi, đây không phải là vấn đề đối với tôi.

Giải pháp của Ben B là hoàn hảo và là giải pháp thanh lịch nhất, mặc dù để gỡ lỗi là rất nhiều dòng mã, vì vậy tôi chọn sử dụng sửa đổi nhỏ của mình đối với Kelsey's. Có thể đáng để tạo một hệ thống như thủ tục được lưu trữ trong msdb cho mã của Ben B có thể được sử dụng lại và được gọi trong một dòng?

Rất tiếc, mã của Alfoks không hoạt động vì điều đó sẽ dễ dàng hơn.


Tôi vừa thêm giải pháp của Ben B làm thủ tục lưu trữ tạm thời. Giữ cho các tập lệnh của tôi gọn gàng hơn một chút, nhưng tôi đồng ý rằng có rất nhiều dòng để gỡ lỗi.
Zarepheth

4

Bạn có thể sử dụng cái này

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

Tôi vừa tạo một SP từ câu trả lời tuyệt vời của Ben :

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

Tuyệt vời, đúng như những gì tôi đang tìm kiếm!
kooch

3
tạo thủ tục dbo.PrintMax @text nvarchar (max)
như
bắt đầu
    khai báo @i int, @newline nchar (2), @print varchar (max); 
    đặt @newline = nchar (13) + nchar (10);
    chọn @i = charindex (@newline, @text);
    trong khi (@i> 0)
    bắt đầu
        chọn @print = substring (@ text, 0, @ i);
        while (len (@print)> 8000)
        bắt đầu
            in chuỗi con (@ print, 0,8000);
            chọn @print = substring (@ print, 8000, len (@print));
        kết thúc
        in @print;
        chọn @text = substring (@ text, @ i + 2, len (@text));
        chọn @i = charindex (@newline, @text);
    kết thúc
    in @text;
kết thúc

2

Có một chức năng tuyệt vời được gọi là PrintMax được viết bởi Bennett Dill .

Đây là phiên bản được sửa đổi một chút sử dụng quy trình lưu trữ tạm thời để tránh "phân cực lược đồ" (ý tưởng từ 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;');

Bản trình diễn DBFiddle

BIÊN TẬP:

Sử dụng CREATE OR ALTERchúng tôi có thể tránh được hai cuộc gọi 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


2

Sử dụng Nguồn cấp dòng và khoảng trắng như một điểm ngắt tốt:

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

Làm việc hoàn hảo
Jolley71717

2

Hoặc đơn giản:

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

0

Đây là một phiên bản khác. Điều này trích xuất từng chuỗi con để in từ chuỗi chính thay vì giảm chuỗi chính đi 4000 trên mỗi vòng lặp (điều này có thể tạo ra nhiều chuỗi rất dài - không chắc chắn).

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

Điều này sẽ hoạt động tốt đây chỉ là một cải tiến của các câu trả lời trước đó.

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

Nếu mã nguồn không có vấn đề với LF sẽ được thay thế bằng CRLF, thì Không cần gỡ lỗi bằng cách làm theo các đầu ra mã đơn giản.

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

Phiên bản PrintMax của tôi để ngăn chặn lỗi ngắt dòng trên đầu ra:


    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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.