I understand that there is a 4000 max set for NVARCHAR(MAX)
Your understanding is wrong. nvarchar(max)
can store up to (and beyond sometimes) 2GB of data (1 billion double byte characters).
From nchar and nvarchar in Books online the grammar is
nvarchar [ ( n | max ) ]
The |
character means these are alternatives. i.e. you specify either n
or the literal max
.
If you choose to specify a specific n
then this must be between 1 and 4,000 but using max
defines it as a large object datatype (replacement for ntext
which is deprecated).
In fact in SQL Server 2008 it seems that for a variable the 2GB limit can be exceeded indefinitely subject to sufficient space in tempdb
(Shown here)
Regarding the other parts of your question
Truncation when concatenating depends on datatype.
varchar(n) + varchar(n)
will truncate at 8,000 characters.
nvarchar(n) + nvarchar(n)
will truncate at 4,000 characters.
varchar(n) + nvarchar(n)
will truncate at 4,000 characters. nvarchar
has higher precedence so the result is nvarchar(4,000)
[n]varchar(max)
+ [n]varchar(max)
won't truncate (for < 2GB).
varchar(max)
+ varchar(n)
won't truncate (for < 2GB) and the result will be typed as varchar(max)
.
varchar(max)
+ nvarchar(n)
won't truncate (for < 2GB) and the result will be typed as nvarchar(max)
.
nvarchar(max)
+ varchar(n)
will first convert the varchar(n)
input to nvarchar(n)
and then do the concatenation. If the length of the varchar(n)
string is greater than 4,000 characters the cast will be to nvarchar(4000)
and truncation will occur.
Datatypes of string literals
If you use the N
prefix and the string is <= 4,000 characters long it will be typed as nvarchar(n)
where n
is the length of the string. So N'Foo'
will be treated as nvarchar(3)
for example. If the string is longer than 4,000 characters it will be treated as nvarchar(max)
If you don't use the N
prefix and the string is <= 8,000 characters long it will be typed as varchar(n)
where n
is the length of the string. If longer as varchar(max)
For both of the above if the length of the string is zero then n
is set to 1.
Newer syntax elements.
1. The CONCAT
function doesn't help here
DECLARE @A5000 VARCHAR(5000) = REPLICATE('A',5000);
SELECT DATALENGTH(@A5000 + @A5000),
DATALENGTH(CONCAT(@A5000,@A5000));
The above returns 8000 for both methods of concatenation.
2. Be careful with +=
DECLARE @A VARCHAR(MAX) = '';
SET @A+= REPLICATE('A',5000) + REPLICATE('A',5000)
DECLARE @B VARCHAR(MAX) = '';
SET @B = @B + REPLICATE('A',5000) + REPLICATE('A',5000)
SELECT DATALENGTH(@A),
DATALENGTH(@B);`
Returns
-------------------- --------------------
8000 10000
Note that @A
encountered truncation.
How to resolve the problem you are experiencing.
You are getting truncation either because you are concatenating two non max
datatypes together or because you are concatenating a varchar(4001 - 8000)
string to an nvarchar
typed string (even nvarchar(max)
).
To avoid the second issue simply make sure that all string literals (or at least those with lengths in the 4001 - 8000 range) are prefaced with N
.
To avoid the first issue change the assignment from
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'Foo' + 'Bar' + ...;
To
DECLARE @SQL NVARCHAR(MAX) = '';
SET @SQL = @SQL + N'Foo' + N'Bar'
so that an NVARCHAR(MAX)
is involved in the concatenation from the beginning (as the result of each concatenation will also be NVARCHAR(MAX)
this will propagate)
Avoiding truncation when viewing
Make sure you have "results to grid" mode selected then you can use
select @SQL as [processing-instruction(x)] FOR XML PATH?
The SSMS options allow you to set unlimited length for XML
results. The processing-instruction
bit avoids issues with characters such as <
showing up as <
.