Похоже, что это не может быть решено в чистом T-SQL, так как не позволяет и CHARINDEXне PATINDEXпозволяет использовать более 8000 байтов в строке «для поиска» (т. Е. Не более 8000 VARCHARили 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
Оба этих запроса возвращают следующую ошибку:
Сообщение 8152, уровень 16, состояние 10, строка xxxxx
Строка или двоичные данные будут обрезаны.
И, уменьшая 7000в любом из этих запросов, чтобы 3999избавиться от ошибки. Значение 4000в обоих случаях также будет ошибочным (из-за дополнительного N'Z'символа в начале).
ОДНАКО, это может быть достигнуто с помощью SQLCLR. Довольно просто создать скалярную функцию, которая принимает два входных параметра типа NVARCHAR(MAX).
Следующий пример иллюстрирует эту возможность с помощью бесплатной версии библиотеки SQL # SQLCLR (которую я создал, но String_Contains снова доступен в бесплатной версии :-).
String_Contains скалярных UDF в настоящее время имеют @SearchValueвход параметры , как NVARCHAR(4000)вместо того , чтобы NVARCHAR(MAX)(я не должен подумать , люди будут искать для строк более 4000 символов ;-) но это очень легко изменить, сделав следующее изменение одноразового (после SQL # было установлено, конечно):
GO
ALTER FUNCTION [SQL#].[String_Contains](@StringValue [NVARCHAR](MAX),
@SearchValue [NVARCHAR](MAX))
RETURNS [BIT]
WITH EXECUTE AS CALLER
AS EXTERNAL NAME [SQL#].[STRING].[Contains];
GO
НАСТРОИТЬ
-- 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;
ИСПЫТАНИЯ
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
Помните, что String_Contains использует сравнение с учетом всего (регистр, акцент, кана и ширина).
where st.text like '%MY_QUERY%CHARS%' ESCAPE '?'