4

我在由 SSRS 报告调用的先前工作的存储过程中遇到错误,我已将其追溯到由存储过程调用的标量函数中的 LIKE 语句,并结合了 7000+ NVARCHAR(MAX) 字符串. 它类似于:

消息 8152,第 16 级,状态 10,第 14 行
字符串或二进制数据将被截断。

我可以使用以下代码重现它:

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'

这是怎么回事?无论如何围绕这个限制,还是有充分的理由?谢谢。

4

2 回答 2

7

您也可以在没有可怕循环的情况下重现它:

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';

结果:

OK
Msg 8152, Level 16, State 10, Line 30
字符串或二进制数据将被截断。

无论如何,原因在(强调我的)的文档中明确说明:LIKE

match_expression [ NOT ] LIKE pattern [ ESCAPE escape_character ]

...

pattern 是要在match_expression

中搜索的特定字符串,可以包含以下有效的通配符。模式最多可以是 8,000 字节。

8,000 个字节被 4,000 个 Unicode 字符用完。

我建议比较前 4,000 个字符可能就足够了:

WHERE column LIKE LEFT(@param, 4000) + '%';

我无法想象您想要比较整个事情的任何场景。有多少个字符串包含相同的前 4000 个字符,但字符 4001 不同?如果这确实是一个要求,我想您可以尽最大努力在David 指出的 Connect 项目中确定。

一个更简单(尽管可能在计算上更昂贵)的解决方法可能是:

IF CONVERT(VARBINARY(MAX), @name1) = CONVERT(VARBINARY(MAX), @name2)
  PRINT 'OK';

我建议通过比较大字符串来修复设计并停止识别行会更好。真的没有其他方法可以识别您所追求的行吗?这就像通过测试所有杯架中所有 Dunkin Donuts 杯子的 DNA 来在停车场找到你的车,而不仅仅是检查车牌。

于 2013-09-11T21:09:28.423 回答
2

我现在有同样的问题,我相信我的情况 - 你想比较两个超过 4000 个字符的字符串 - 是一种可能的情况:-)。在我的情况下,我从特定表的 NVARCHAR(MAX) 字段中的不同表中收集大量数据,以便能够使用全文搜索该数据。使用 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

当全文数据大于 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
于 2018-12-18T08:42:41.067 回答