总之,我有一个很大的(不可避免的)动态SQL查询。由于选择标准中的字段数,包含动态SQL的字符串增长超过4000个字符。现在,我知道NVARCHAR(MAX)
有4000最大值设置,但是查看Server Profiler中执行的SQL语句
DELARE @SQL NVARCHAR(MAX);
SET @SQL = 'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
似乎工作(!?),对于另一个同样大的查询,它会抛出一个与此4000限制(!?)相关的错误,它基本上修剪了这个4000限制之后的所有SQL,并且给我留下了语法错误。尽管在分析器中,它仍然显示这个动态SQL查询(!?)。
究竟发生了什么,我应该将这个@SQL变量转换为VARCHAR并继续使用它吗?
谢谢你的时间。
PS。能够打印超过4000个字符来查看这些大查询也是很好的。以下限制为4000
SELECT CONVERT(XML, @SQL);
PRINT(@SQL);
还有其他很酷的方式吗?
据我所知,
NVARCHAR(MAX)
有4000最大值
你的理解是错误的。 nvarchar(max)
可存储最多2GB的数据(有时超过2GB)(10亿个双字节字符)。
来自nchar and nvarchar的书籍在线语法是
nvarchar [ ( n | max ) ]
|
字符意味着这些是替代品。即你指定n
或文字max
。
如果您选择指定特定的n
,则必须介于1和4,000之间,但使用max
将其定义为大对象数据类型(替换为不推荐使用的ntext
)。
实际上在SQL Server 2008中,似乎对于变量,可以在tempdb
(Shown here)中无限期地超出2GB限制。
关于你问题的其他部分
varchar(n) + varchar(n)
将截断8,000个字符。nvarchar(n) + nvarchar(n)
将截断4,000个字符。varchar(n) + nvarchar(n)
nvarchar
+ nvarchar(4,000)
不会截断(<2GB)。[n]varchar(max)
+ [n]varchar(max)
不会截断(<2GB),结果将输入为varchar(max)
。varchar(n)
+ varchar(max)
不会截断(<2GB),结果将输入为varchar(max)
。nvarchar(n)
+ nvarchar(max)
将首先将nvarchar(max)
输入转换为varchar(n)
,然后进行连接。如果varchar(n)
字符串的长度大于4,000个字符,则强制转换为nvarchar(n)
并发生截断。如果你使用varchar(n)
前缀并且字符串<= 4,000个字符长,它将被输入为nvarchar(4000)
,其中N
是字符串的长度。因此,nvarchar(n)
将被视为n
。如果字符串超过4,000个字符,则将其视为N'Foo'
如果你不使用nvarchar(3)
前缀并且字符串长度<= 8,000个字符,那么它将被输入为nvarchar(max)
,其中N
是字符串的长度。如果varchar(n)
更长
对于上述两种情况,如果字符串的长度为零,则n
设置为1。
1. varchar(max)
功能在这里没有帮助
n
以上两种连接方法都返回8000。
2.小心CONCAT
DECLARE @A5000 VARCHAR(5000) = REPLICATE('A',5000);
SELECT DATALENGTH(@A5000 + @A5000),
DATALENGTH(CONCAT(@A5000,@A5000));
返回
+=
请注意,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);`
遇到了截断。
您正在截断,因为您将两个非-------------------- --------------------
8000 10000
数据类型连接在一起,或者因为您将@A
字符串连接到max
类型字符串(甚至varchar(4001 - 8000)
)。
为了避免第二个问题,只需确保所有字符串文字(或至少那些长度在4001 - 8000范围内)以nvarchar
开头。
要避免第一个问题,请更改分配
nvarchar(max)
至
N
所以DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'Foo' + 'Bar' + ...;
从头开始参与连接(因为每个连接的结果也将是DECLARE @SQL NVARCHAR(MAX) = '';
SET @SQL = @SQL + N'Foo' + N'Bar'
这将传播)
确保选择了“结果到网格”模式,然后才能使用
NVARCHAR(MAX)
SSMS选项允许您为NVARCHAR(MAX)
结果设置无限长度。 select @SQL as [processing-instruction(x)] FOR XML PATH
位避免了像XML
这样的字符出现processing-instruction
的问题。
好的,所以如果以后的问题是你有一个大于允许大小的查询(如果它继续增长可能会发生),你将不得不将其分成块并执行字符串值。所以,假设你有一个如下存储过程:
<
您也必须使用nvarchar文本。这意味着你必须在你的大量字符串之前只有一个“N”,就是这样!没有限制了
<
接受的答案对我有所帮助,但在连接涉及案例陈述的varchars时,我被绊倒了。我知道OP的问题不涉及案例陈述,但我认为这对于像我这样的人在这里发布,同时努力构建涉及案例陈述的长动态SQL语句会有所帮助。
当使用带有字符串连接的case语句时,接受的答案中提到的规则将独立地应用于case语句的每个部分。
CREATE PROCEDURE ExecuteMyHugeQuery
@SQL VARCHAR(MAX) -- 2GB size limit as stated by Martin Smith
AS
BEGIN
-- Now, if the length is greater than some arbitrary value
-- Let's say 2000 for this example
-- Let's chunk it
-- Let's also assume we won't allow anything larger than 8000 total
DECLARE @len INT
SELECT @len = LEN(@SQL)
IF (@len > 8000)
BEGIN
RAISERROR ('The query cannot be larger than 8000 characters total.',
16,
1);
END
-- Let's declare our possible chunks
DECLARE @Chunk1 VARCHAR(2000),
@Chunk2 VARCHAR(2000),
@Chunk3 VARCHAR(2000),
@Chunk4 VARCHAR(2000)
SELECT @Chunk1 = '',
@Chunk2 = '',
@Chunk3 = '',
@Chunk4 = ''
IF (@len > 2000)
BEGIN
-- Let's set the right chunks
-- We already know we need two chunks so let's set the first
SELECT @Chunk1 = SUBSTRING(@SQL, 1, 2000)
-- Let's see if we need three chunks
IF (@len > 4000)
BEGIN
SELECT @Chunk2 = SUBSTRING(@SQL, 2001, 2000)
-- Let's see if we need four chunks
IF (@len > 6000)
BEGIN
SELECT @Chunk3 = SUBSTRING(@SQL, 4001, 2000)
SELECT @Chunk4 = SUBSTRING(@SQL, 6001, (@len - 6001))
END
ELSE
BEGIN
SELECT @Chunk3 = SUBSTRING(@SQL, 4001, (@len - 4001))
END
END
ELSE
BEGIN
SELECT @Chunk2 = SUBSTRING(@SQL, 2001, (@len - 2001))
END
END
-- Alright, now that we've broken it down, let's execute it
EXEC (@Chunk1 + @Chunk2 + @Chunk3 + @Chunk4)
END
DELARE @SQL NVARCHAR(MAX);
SET @SQL = N'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO