No parece que esto pueda resolverse en T-SQL puro ya que CHARINDEXni PATINDEXpermite usar más de 8000 bytes en la cadena "para buscar" (es decir, un máximo de 8000 VARCHARo 4000 NVARCHARcaracteres). Esto se puede ver en las siguientes pruebas:
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
Ambas consultas devuelven el siguiente error:
Msg 8152, Nivel 16, Estado 10, Línea xxxxx La
cadena o los datos binarios se truncarían.
Y, al reducir la consulta 7000en cualquiera de esas consultas, 3999se elimina el error. Un valor de 4000en ambos casos también producirá un error (debido al N'Z'carácter adicional al principio).
SIN EMBARGO, esto se puede lograr usando SQLCLR. Es bastante simple crear una función escalar que acepte dos parámetros de entrada de tipo NVARCHAR(MAX).
El siguiente ejemplo ilustra esta capacidad usando la versión gratuita de la biblioteca SQL # SQLCLR (que creé, pero String_Contains está nuevamente disponible en la versión gratuita :-).
Los String_Contains escalares UDF tiene actualmente el @SearchValueparámetro de entrada como NVARCHAR(4000)en lugar de NVARCHAR(MAX)(No debe haber pensado que la gente estaría buscando para cadenas de más de 4000 caracteres ;-), pero que es muy fácil cambiar al hacer el siguiente cambio de una sola vez (después de SQL # ha sido instalado, por supuesto):
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
PREPARAR
-- 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;
PRUEBAS
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
Tenga en cuenta que String_Contains está utilizando una comparación sensible a todo (mayúsculas, acento, Kana y ancho).
where st.text like '%MY_QUERY%CHARS%' ESCAPE '?'