克服 LIKE 字符长度限制

Dan*_*inu 13 sql-server t-sql like sql-server-2016

通过在此处阅读此LIKE 字符长度限制,看起来我无法在 LIKE 子句中发送超过 ~4000 个字符的文本。

我正在尝试从特定查询的查询计划缓存中获取查询计划。

SELECT *
FROM sys.dm_exec_cached_plans AS cp 
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp 
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
where st.text like '%MY_QUERY_LONGER_THAN_4000_CHARS%' ESCAPE '?'
Run Code Online (Sandbox Code Playgroud)

如果里面的查询LIKE超过 4000 个字符,那么即使我的查询在缓存计划中,我也会得到 0 个结果。(我期待至少有一个错误)。

有没有办法解决这个问题或采取不同的方式?我有可能是 >10000字符长的查询,看起来我无法通过LIKE.

Sol*_*zky 9

这似乎不能在纯 T-SQL 中解决,因为既不允许CHARINDEXPATINDEX不允许在“搜索”字符串中使用超过 8000 个字节(即最多 8000VARCHAR或 4000 个NVARCHAR字符)。这可以在以下测试中看到:

SELECT 1 WHERE CHARINDEX(N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 7000),
                         N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 6000)) > 0

SELECT 1 WHERE PATINDEX(N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 7000),
                        N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 6000)) > 0
Run Code Online (Sandbox Code Playgroud)

这两个查询都返回以下错误:

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

并且,减少7000这些查询中的任何一个以3999消除错误。4000在这两种情况下的值也会出错(由于N'Z'开头的额外字符)。

但是,这可以使用 SQLCLR 来完成。创建一个接受两个类型为 的输入参数的标量函数相当简单NVARCHAR(MAX)

下面的示例使用SQL# SQLCLR 库的免费版本(我创建了它,但String_Contains在免费版本中再次可用 :-)说明了这种能力。

设置

-- DROP TABLE #ContainsData;
CREATE TABLE #ContainsData
(
  ContainsDataID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  Col1 NVARCHAR(MAX) NOT NULL
);

INSERT INTO #ContainsData ([Col1])
VALUES (N'Q' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 15000)),
       (N'W' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 20000)),
       (N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 70000));

-- verify the lengths being over 8000
SELECT tmp.[ContainsDataID], tmp.[Col1], DATALENGTH(tmp.[Col1])
FROM   #ContainsData tmp;
Run Code Online (Sandbox Code Playgroud)

测试

SELECT tmp.[ContainsDataID], tmp.[Col1], DATALENGTH(tmp.[Col1])
FROM   #ContainsData tmp
WHERE  SQL#.String_Contains(tmp.[Col1], REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 15100)) = 1;
-- IDs returned: 2 and 3

SELECT tmp.[ContainsDataID], tmp.[Col1], DATALENGTH(tmp.[Col1])
FROM   #ContainsData tmp
WHERE  SQL#.String_Contains(tmp.[Col1], REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 26100)) = 1;
-- IDs returned: 3
Run Code Online (Sandbox Code Playgroud)

请记住,String_Contains使用对所有内容敏感(大小写、重音、假名和宽度)的比较。