[英]SQL NVARCHAR and VARCHAR Limits
所有,我有一個大的(不可避免的)動態 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 並繼續嗎?
謝謝你的時間。
附言。 能夠打印出超過 4000 個字符來查看這些大查詢也很好。 以下限制為 4000
SELECT CONVERT(XML, @SQL);
PRINT(@SQL);
還有其他很酷的方法嗎?
我知道
NVARCHAR(MAX)
最大設置為 4000
你的理解是錯誤的。 nvarchar(max)
可以存儲(有時甚至超過)2GB 的數據(10 億個雙字節字符)。
從在線書籍中的nchar 和 nvarchar語法是
nvarchar [ ( n | max ) ]
的|
字符意味着這些是替代品。 即您指定n
或文字max
。
如果您選擇指定特定的n
那么它必須介於 1 和 4,000 之間,但使用max
將其定義為大對象數據類型(替換已棄用的ntext
)。
事實上,在 SQL Server 2008 中,似乎可以無限期地超過 2GB 的變量限制, tempdb
足夠的空間(如圖所示)
關於你問題的其他部分
varchar(n) + varchar(n)
將在 8,000 個字符處截斷。nvarchar(n) + nvarchar(n)
將截斷 4,000 個字符。varchar(n) + nvarchar(n)
將截斷 4,000 個字符。 nvarchar
具有更高的優先級,所以結果是nvarchar(4,000)
[n]varchar(max)
+ [n]varchar(max)
不會截斷(對於 < 2GB)。varchar(max)
+ varchar(n)
不會截斷(對於 < 2GB),結果將被輸入為varchar(max)
。varchar(max)
+ nvarchar(n)
不會截斷(對於 < 2GB),結果將被輸入為nvarchar(max)
。nvarchar(max)
+ varchar(n)
將首先將varchar(n)
輸入轉換為nvarchar(n)
,然后進行連接。 如果varchar(n)
字符串的長度大於 4,000 個字符,則nvarchar(4000)
為nvarchar(4000)
並且會發生截斷。如果您使用N
前綴並且字符串長度 <= 4,000 個字符,它將被輸入為nvarchar(n)
,其中n
是字符串的長度。 因此,例如, N'Foo'
將被視為nvarchar(3)
。 如果字符串超過 4,000 個字符,它將被視為nvarchar(max)
如果您不使用N
前綴並且字符串 <= 8,000 個字符長,它將被輸入為varchar(n)
,其中n
是字符串的長度。 如果長為varchar(max)
對於上述兩種情況,如果字符串的長度為零,則n
設置為 1。
1. CONCAT
函數在這里沒有幫助
DECLARE @A5000 VARCHAR(5000) = REPLICATE('A',5000);
SELECT DATALENGTH(@A5000 + @A5000),
DATALENGTH(CONCAT(@A5000,@A5000));
上述兩種連接方法都返回 8000。
2.小心+=
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)
字符串連接到nvarchar
類型的字符串(甚至nvarchar(max)
)。
為了避免第二個問題,只需確保所有字符串文字(或至少那些長度在 4001 - 8000 范圍內的文字)都以N
開頭。
為了避免第一個問題,將分配從
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'Foo' + 'Bar' + ...;
到
DECLARE @SQL NVARCHAR(MAX) = '';
SET @SQL = @SQL + N'Foo' + N'Bar'
以便NVARCHAR(MAX)
從一開始就參與連接(因為每個連接的結果也將是NVARCHAR(MAX)
這將傳播)
確保您選擇了“結果到網格”模式,然后您可以使用
select @SQL as [processing-instruction(x)] FOR XML PATH
SSMS 選項允許您為XML
結果設置無限長度。 processing-instruction
位避免了諸如<
顯示為<
等字符的問題<
.
好的,所以如果后面的問題是您的查詢大於允許的大小(如果它不斷增長可能會發生這種情況),您將不得不將其分成塊並執行字符串值。 因此,假設您有一個如下所示的存儲過程:
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
您也必須使用 nvarchar 文本。 這意味着你必須在你的大字符串之前簡單地有一個“N”,就是這樣! 沒有限制了
DELARE @SQL NVARCHAR(MAX);
SET @SQL = N'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
接受的答案對我有幫助,但在連接涉及 case 語句的 varchars 時我被絆倒了。 我知道 OP 的問題不涉及 case 語句,但我認為這對於像我這樣在努力構建涉及 case 語句的長動態 SQL 語句時最終來到這里的其他人來說會有所幫助。
當使用帶有字符串連接的 case 語句時,接受的答案中提到的規則獨立地適用於 case 語句的每個部分。
declare @l_sql varchar(max) = ''
set @l_sql = @l_sql +
case when 1=1 then
--without this correction the result is truncated
--CONVERT(VARCHAR(MAX), '')
+REPLICATE('1', 8000)
+REPLICATE('1', 8000)
end
print len(@l_sql)
declare @p varbinary(max)
set @p = 0x
declare @local table (col text)
SELECT @p = @p + 0x3B + CONVERT(varbinary(100), Email)
FROM tbCarsList
where email <> ''
group by email
order by email
set @p = substring(@p, 2, 100000)
insert @local values(cast(@p as varchar(max)))
select DATALENGTH(col) as collen, col from @local
result collen > 8000, length col value is more than 8000 chars
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.