Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nvarchar(max) still being truncated

So I'm writing a stored procedure in MS SQL Server 2008. It's a really long query and I have to write it dynamically, so I create a variable called @Query and make it of type NVARCHAR(MAX). Now, I have been told that in modern versions of SQL Server, NVARCHAR(MAX) can hold a ridiculous amount of data, way more than the original 4000 character maximum. However, @Query is still getting truncated to 4000 characters when I try to print it out.

DECLARE @Query NVARCHAR(max); SET @Query = 'SELECT...' -- some of the query gets set here SET @Query = @Query + '...' -- more query gets added on, etc.  -- later on... PRINT LEN(@Query) -- Prints out 4273, which is correct as far as I can tell PRINT @Query      -- Truncates value to 4000 characters EXEC sp_executesql @Query -- totally crashes due to malformed (truncated) query 

Am I doing something incorrectly, or am I completely wrong about how NVARCHAR(MAX) works?

like image 290
Andrew Avatar asked Jan 28 '11 22:01

Andrew


People also ask

Why is my varchar Max variable getting truncated?

The reason this happens is that SQL Server doesn't want to store something as VARCHAR(MAX) if none of the variable's components are defined as VARCHAR(MAX).

How can I store more than 8000 characters in SQL variable?

MsSql as of 2012 supports Ntext for example that allows you to go beyond 8000 characters in a variable. The way to solve this is to make multiple variables or multiple rows in a table that you can iterate through.

How do I declare nvarchar Max?

You can assign a longer string to an nvarchar(max) variable, but as soon as you include + in the expression (such as + CASE ... END + ), then the expression result is limited to 4,000 chars. One way to fix this is to use CONCAT instead of +.

Does nvarchar Max have a limit?

The max size for a column of type NVARCHAR(MAX) is 2 GByte of storage. Since NVARCHAR uses 2 bytes per character, that's approx. 1 billion characters.


2 Answers

The problem is with implicit conversion.

If you have Unicode/nChar/nVarChar values you are concatenating, then SQL Server will implicitly convert your string to nVarChar(4000), and it is unfortunately too dumb to realize it will truncate your string or even give you a Warning that data has been truncated for that matter!

When concatenating long strings (or strings that you feel could be long) always pre-concatenate your string building with CAST('' as nVarChar(MAX)) like so:

SET @Query = CAST('' as nVarChar(MAX))--Force implicit conversion to nVarChar(MAX)            + 'SELECT...'-- some of the query gets set here            + '...'-- more query gets added on, etc. 

What a pain and scary to think this is just how SQL Server works. :(

I know other workarounds on the web say to break up your code into multiple SET/SELECT assignments using multiple variables, but this is unnecessary given the solution above.

For those who hit an 8000 character max, it was probably because you had no Unicode so it was implicitly converted to VarChar(8000).

Warning:
You still Cannot have a Single Unbroken Literal String Larger than 4000 (or 8000 for VarChar).
Literal Strings are those you hard-code and wrap in apostrophe's.
You must Break those Strings up or SQL Server will Truncate each one BEFORE concatenating.
I add ' + ' every 20 lines (or so) to make sure I do not go over.
That's an average of at most 200 characters per line - but remember, spaces still count!

Explanation:
What's happening behind the scenes is that even though the variable you are assigning to uses (MAX), SQL Server will evaluate the right-hand side of the value you are assigning first and default to nVarChar(4000) or VarChar(8000) (depending on what you're concatenating). After it is done Concatenating and figuring out the value (and after truncating it for you) it then converts it into (MAX) when assigning it to your variable, but by then it is too late.

like image 53
MikeTeeVee Avatar answered Oct 13 '22 14:10

MikeTeeVee


Problem seems to be associated with the SET statement. I think the expression can't be more than 4,000 bytes in size. There is no need to make any changes to any settings if all you are trying to do is to assign a dynamically generated statement that is more than 4,000 characters. What you need to do is to split your assignment. If your statement is 6,000 characters long, find a logical break point and then concatenate second half to the same variable. For example:

SET @Query = 'SELECT ....' [Up To 4,000 characters, then rest of statement as below]  SET @Query = @Query + [rest of statement] 

Now run your query as normal i.e. EXEC ( @Query )

like image 32
kannas Avatar answered Oct 13 '22 12:10

kannas