我在SSRS报告调用的以前工作的存储过程中遇到错误,并且我已将其追溯到存储过程调用的标量函数中的LIKE语句,并结合7000+ NVARCHAR( MAX)字符串。它类似于:
Msg 8152, Level 16, State 10, Line 14 String or binary data would be truncated.
我可以使用以下代码重现它:
DECLARE @name1 NVARCHAR(MAX) = ''
DECLARE @name2 NVARCHAR(MAX) = ''
DECLARE @count INT = 4001
WHILE @count > 0
BEGIN
SET @name1 = @name1 + 'a'
SET @name2 = @name2 + 'a'
SET @count = @count - 1
END
SELECT LEN(@name1)
IF @name1 LIKE @name2
PRINT 'OK'
这笔交易是什么?反正有这个限制,还是有充分的理由?感谢。
答案 0 :(得分:6)
你也可以在没有可怕循环的情况下重现它:
DECLARE @name1 NVARCHAR(MAX), @name2 NVARCHAR(MAX);
SET @name1 = REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 4000);
SET @name2 = @name1;
IF @name1 LIKE @name2
PRINT 'OK';
SELECT @name1 += N'a', @name2 += N'a';
IF @name1 LIKE @name2
PRINT 'OK';
结果:
行
Msg 8152,Level 16,State 10,Line 30
字符串或二进制数据将被截断。
无论如何,原因是clearly stated in the documentation for LIKE
(强调我的):
match_expression [NOT] LIKE模式[ESCAPE escape_character]
...
模式
是在 match_expression 中搜索的特定字符串,并且可以包含以下有效通配符。 模式最多可以包含8,000个字节。
8,000个字节用完4,000个Unicode字符。
我建议比较前4,000个字符可能就足够了:
WHERE column LIKE LEFT(@param, 4000) + '%';
我无法想象你想要比较整个事物的任何场景;多少个字符串包含相同的前4000个字符,但字符4001是不同的?如果这确实是一个要求,我想你可以在the Connect item David pointed out中找到很长的篇幅。
更简单(虽然计算成本更高)的解决方法可能是:
IF CONVERT(VARBINARY(MAX), @name1) = CONVERT(VARBINARY(MAX), @name2)
PRINT 'OK';
我建议通过比较大字符串来修复设计并停止识别行会好得多。真的没有办法确定你所追求的那一行吗?这就像在停车场通过测试所有杯架中所有Dunkin甜甜圈杯子的DNA来找到你的车,而不仅仅是检查车牌。
答案 1 :(得分:2)
我现在有相同的问题,我确实相信我的情况-您要比较两个字符串超过4000个字符的情况-:)。 在我的情况下,我正在从特定表的NVARCHAR(MAX)字段中的不同表中收集大量数据,以便能够使用FullText搜索该数据。使用MERGE语句使该表保持同步,将所有内容转换为NVARCHAR(MAX)。 所以我的MERGE语句看起来像这样:
MERGE MyFullTextTable AS target
USING (
SELECT --Various stuff from various tables, casting it as NVARCHAR(MAX)
...
) AS source (IndexColumn, FullTextColumn)
ON (target.IndexColumn = source.IndexColumn)
WHEN MATCHED AND source.FullTextColumn NOT LIKE target.FullTextColumn THEN
UPDATE SET FullTextColumn = source.FullTextColumn
WHEN NOT MATCHED THEN
INSERT (IndexColumn, FullTextColumn)
VALUES (source.IndexColumn, source.FullTextColumn)
OUTPUT -- Some stuff
由于Full数据大于4000个字符时的LIKE比较,这将产生错误。
所以我创建了一个进行比较的函数。尽管它不是防弹的,但对我有用。您也可以将数据分成4000个字符的块,然后比较每个块,但是对我而言(目前)将前4000个字符与长度进行比较就足够了...
所以合并语句看起来像:
MERGE MyFullTextTable AS target
USING (
SELECT --Various stuff from various tables, casting it as NVARCHAR(MAX)
...
) AS source (IndexColumn, FullTextColumn)
ON (target.IndexColumn = source.IndexColumn)
WHEN MATCHED AND udfCompareTwoTexts(source.FullTextColumn, target.FullTextColumn) = 1 THEN
UPDATE SET FullTextColumn = source.FullTextColumn
WHEN NOT MATCHED THEN
INSERT (IndexColumn, FullTextColumn)
VALUES (source.IndexColumn, source.FullTextColumn)
OUTPUT -- Some stuff
函数如下:
ALTER FUNCTION udfCompareTwoTexts
(
@Value1 AS NVARCHAR(MAX),
@Value2 AS NVARCHAR(MAX)
)
RETURNS BIT
AS
BEGIN
DECLARE @ReturnValue AS BIT = 0
IF LEN(@Value1) > 4000 OR LEN(@Value2) > 4000
BEGIN
IF LEN(@Value1) = LEN(@Value2) AND LEFT(@Value1, 4000) LIKE LEFT(@Value2, 4000)
SET @ReturnValue = 1
ELSE
SET @ReturnValue = 0
END
ELSE
BEGIN
IF @Value1 LIKE @Value2
SET @ReturnValue = 1
ELSE
SET @ReturnValue = 0
END
RETURN @ReturnValue;
END
GO