No parece que esto pueda resolverse en T-SQL puro ya que CHARINDEX
ni PATINDEX
permite usar más de 8000 bytes en la cadena "para buscar" (es decir, un máximo de 8000 VARCHAR
o 4000 NVARCHAR
caracteres). 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 7000
en cualquiera de esas consultas, 3999
se elimina el error. Un valor de 4000
en 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 @SearchValue
pará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 '?'