LIKE语句中的4000个字符限制

时间:2013-09-11 20:51:06

标签: sql sql-server ssrs-2008

我在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'

这笔交易是什么?反正有这个限制,还是有充分的理由?感谢。

2 个答案:

答案 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
相关问题