Ben Dill's Blog

SQL Server - Print MAX

Welcome to my blog, this is post numero uno.  I would like to thank Joe Stagner for hooking me up with a blog on weblogs.asp.net.  My name is Bennett Dill and I am an Application Architect at a market research firm in Cincinnati Ohio.  I have been programming for about ten years starting with ASP Classic and VB 6 right on up to C# and ASP.NET.  I don't know if I'm going to be a good blogger, but I'll give it a whirl.

Ok, so what about SQL Server & Print MAX?  From time-to-time, I deal with dynamic SQL.    Some of the dynamic queries get large, so NVARCHAR(MAX) was a welcome addition in MS SQL 2005.  Recently I encountered an error with a dynamic query, normally this isn’t a problem, print the SQL displaying the interpolated values and fix the typo.  Herein lies the problem, this SQL actually surpassed the NVARCHAR(4000), cool, the first SQL exceeding the old limit.  Why is that a problem?  Well, it seems as though the PRINT statement is still limited to NVARCHAR(4000)  or VARCHAR(8000).

I needed a fix for this and the fix needed to act just like the built-in PRINT function.  Breaking every 4000 characters and printing wasn’t an option because it would insert line breaks at unknown locations because each PRINT statement adds a line break.  While there may be an option to PRINT without a line break, I don’t know it, and will need to quickly update this post when someone points it out ;-).

Until then the fix is as follows, for a given string:

  1. Walk it in 4000 character chunks
  2. Find the last line break
  3. Note it’s position
  4. Print up to it
  5. Skip it
  6. Go back to step 1

Here is the SQL used to accomplish this (I apologize for not having a [code] style yet):

CREATE PROCEDURE [dbo].[PrintMax]
(
 @iInput NVARCHAR(MAX)
)
AS
BEGIN
 -- nothing we can do with null data
 IF (@iInput IS NULL) BEGIN
  RETURN;
 END;

 -- This procedure was created to properly print
 -- nvarchar(max) since the print statement can
 -- only handle NVARCHAR(4000), we break the
 -- input down into 4000 byte blocks and print
 -- upto the last linebreak before the 4000 byte cutoff
 DECLARE @ReversedData NVARCHAR(MAX)
  ,@LineBreakIndex INT
  ,@SearchLength INT;

 -- if the search length is less than the first occurance
 -- of a line break, the data will be printed with a line break
 -- at the SearchLength position even though there should not be
 -- a break in the data there.
 SET @SearchLength = 4000;

 -- only loop while the input is greater than the search length
 WHILE (LEN(@iInput) > @SearchLength) BEGIN
  -- obtain and reverse the input upto the search length
  SET @ReversedData = LEFT(@iInput, @SearchLength);
  SET @ReversedData = REVERSE(@ReversedData);

  -- determine the position of the first line break for this piece of data
  SET @LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13), @ReversedData);

  -- print the input only showing data upto the line break
  -- the original linebreak will not be displayed
  PRINT LEFT(@iInput, @SearchLength - @LineBreakIndex + 1);

  -- resize the input removing the data that was displayed and the line break.
  SET @iInput = RIGHT(@iInput, LEN(@iInput) - @SearchLength + @LineBreakIndex - 1);
 END;

 -- if there is any data remaining, print it
 IF (LEN(@iInput) > 0) BEGIN
  PRINT @iInput;
 END;
END;

All feedback is welcome, thanks!

Posted: Sep 29 2007, 09:38 PM by MuteThis | with 20 comment(s)
Filed under:

Comments

Rumtata said:

Great - thanks a lot!

# August 19, 2008 9:08 AM

Jacob said:

Thank you very much for posting this.  The PRINT limitation of 4000 chars was a major issue for me.  Your approach was simple and effective.  

# August 25, 2008 11:01 AM

Mike said:

Thank you so much for the posting.  It really helps me a lot.

By the way, I found some issues regarding the code, which I documented as follows.  My comments proceed with four -

-- determine the position of the first line break for this piece of data

 SET @LineBreakIndex = CHARINDEX(CHAR(13) + CHAR(10), @ReversedData);

---- the checking for line break should've been CHAR(13) + CHAR(10) because the value in @ReversedData is in the reverse order of the original value

 -- print the input only showing data upto the line break

 -- the original linebreak will not be displayed

 PRINT LEFT(@iInput, @SearchLength - @LineBreakIndex - 1);

---- To not to print the linebreak, the length should've been @SearchLength - @LineBreakIndex - 1

 -- resize the input removing the data that was displayed and the line break.

 SET @iInput = RIGHT(@iInput, LEN(@iInput) - @SearchLength + @LineBreakIndex + 1);

---- the length should've been LEN(@iInput) - @SearchLength + @LineBreakIndex + 1

# January 2, 2009 1:30 PM

MuteThis said:

@Mike

Thanks for the feedback.  I'll take a look at your proposed changes and see what the impact is.  It seems like it is working as is, but your reversed 13+10 makes sense.  I can't remember why I'm doing - 1 instead of + 1.  Oh the joy of revisiting long fogotten code :-)

# January 6, 2009 10:28 PM

Ming said:

Thank you so much.

It's great.

This is what I figure out.

# February 10, 2009 9:37 PM

MuteThis said:

@Mike

I'm not sure why I ended up in this procedure, but it looks like CHAR(10) + CHAR(13) is correct.  In Windows (thinking VB here) the constant is vbCRLF.  CR = 13 LF = 10.  So, 10 + 13 is reveresed.

Thanks,

Ben

# April 6, 2009 1:55 PM

Valli said:

great! thanks

# June 4, 2009 6:58 AM

Christian said:

Works great, thanks muchly!

# July 8, 2009 1:56 PM

Jeff said:

So cool!  Thanks man- editing the output of some dynamic script generators was becoming a PAIN.  

# August 4, 2009 4:40 PM

proof said:

Nice proc. Thanks for the sharing!

# August 11, 2009 5:40 PM

Jim K said:

Thank you - could not troubleshoot some dynamic sql that was really long.  

Thanks!@!!!!!!!

# November 20, 2009 7:32 PM

Brady said:

B-E-A-utiful!!!!  Thanks a LOT!!!

# December 9, 2009 1:42 PM

srini said:

This was a life saver for me as I had to use dynamic sql for large queries!

# March 18, 2010 9:29 AM

Luke said:

Awesome. Just what I needed. Thanks!

# May 17, 2010 2:30 AM

T. Phaneuf said:

Nicely done! Thanks.

# July 22, 2010 3:14 PM

Rahu said:

Really Good one and thanks

# December 28, 2010 4:07 AM

Ken Palmer said:

Nice.  Worked like a champ for me.

# March 9, 2011 9:56 AM

Janet Keith said:

THANK YOU!

# March 23, 2011 10:36 AM

Yossi said:

Great post!

# May 3, 2011 10:29 AM

Pallavi said:

Hey Its great......it has solved my long time problem...thanks a ton Bennett

# July 20, 2011 8:13 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)