すべて、大規模な(避けられない)動的SQLクエリがあります。選択基準のフィールド数により、動的SQLを含む文字列は4000文字を超えて増加しています。今、私はNVARCHAR(MAX)
に最大4000が設定されていることを理解していますが、Server Profilerで実行されたSQLを見て
DELARE @SQL NVARCHAR(MAX);
SET @SQL = 'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
動作しているようです(!?)、これも大きい別のクエリでは、この4000の制限に関連するエラーがスローされます(!?)プロファイラーではこれにもかかわらず、この動的SQLクエリはfull(!?)で表示されています。
ここで正確に何が起こっているのですか?この@SQL変数をVARCHARに変換して、それを取得するだけですか
御時間ありがとうございます。
追伸また、これらの大きなクエリを見るために4000以上の文字を印刷できるといいでしょう。以下は4000に制限されています
SELECT CONVERT(XML, @SQL);
PRINT(@SQL);
他のクールな方法はありますか?
NVARCHAR(MAX)
には最大4000が設定されていることを理解しています
あなたの理解は間違っています。 nvarchar(max)
は、最大2GB(場合によってはそれ以上)のデータ(10億個の2バイト文字)を保存できます。
ncharとnvarchar からBooks onlineの文法は
nvarchar [ ( n | max ) ]
|
文字は、これらが代替であることを意味します。つまり、eithern
またはリテラルmax
を指定します。
特定のn
を指定する場合、これは1から4,000の間でなければなりませんが、max
を使用すると、ラージオブジェクトデータ型として定義されます( ntext
は非推奨です)。
実際、SQL Server 2008では、variableの場合、tempdb
に十分なスペースがあれば、2GBの制限を無期限に超えることができます(- ここに表示 )
あなたの質問の他の部分について
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)
になり、切り捨てが発生します。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
が切り捨てられたことに注意してください。
2つの非max
データ型を連結しているため、またはvarchar(4001 - 8000)
文字列をnvarchar
型付き文字列(nvarchar(max)
さえも)に連結しているため、切り捨てられています。
2番目の問題を回避するには、すべての文字列リテラル(または少なくとも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
受け入れられた答えは私を助けましたが、私はケース文を含む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