Límites de SQL NVARCHAR y VARCHAR
Todos, tengo una gran (inevitable) consulta SQL dinámica. Debido al número de campos en los criterios de selección, la cadena que contiene el SQL dinámico está creciendo más de 4000 caracteres. Ahora, entiendo que hay un máximo de 4000 establecido para NVARCHAR(MAX)
, pero mirando el SQL ejecutado en Server Profiler para la instrucción
DELARE @SQL NVARCHAR(MAX);
SET @SQL = 'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
Parece funcionar (!?), para otra consulta que también es grande arroja un error que está asociado con este límite de 4000 (!?), básicamente recorta todo el SQL después de este 4000 límite y me deja con un error de sintaxis. A pesar de esto en el generador de perfiles, está mostrando esta consulta SQL dinámica en full (!?).
¿Qué es exactamente lo que está sucediendo aquí y debería simplemente convertir esta variable @SQL a VARCHAR y seguir adelante con ella?
Gracias por su tiempo.
Ps. También sería bueno poder imprimir más de 4000 caracteres para ver estas grandes consultas. Los siguientes están limitados a 4000
SELECT CONVERT(XML, @SQL);
PRINT(@SQL);
¿Hay alguna otra manera genial?
4 answers
Entiendo que hay un máximo de 4000 establecido para
NVARCHAR(MAX)
Tu entendimiento está equivocado. nvarchar(max)
puede almacenar hasta (y más allá a veces) 2GB de datos (1 billón de caracteres de byte doble).
De nchar y nvarchar en los Libros en línea la gramática es
nvarchar [ ( n | max ) ]
El carácter |
significa que estas son alternativas. es decir, se especifica bien n
o el literal max
.
Si elige especificar un n
específico, entonces esto debe entre 1 y 4.000 pero usando max
lo define como un tipo de datos de objeto grande (reemplazo de ntext
que está en desuso).
De hecho, en SQL Server 2008 parece que para una variable el límite de 2 GB se puede exceder indefinidamente sujeto a espacio suficiente en tempdb
(Se muestra aquí )
Con respecto a las otras partes de su pregunta
El truncamiento al concatenar depende del tipo de datos.
-
varchar(n) + varchar(n)
se truncará a 8.000 caracter. -
nvarchar(n) + nvarchar(n)
se truncará a 4.000 caracteres. -
varchar(n) + nvarchar(n)
se truncará a 4.000 caracteres.nvarchar
tiene mayor precedencia por lo que el resultado esnvarchar(4,000)
-
[n]varchar(max)
+[n]varchar(max)
no se trunca (para -
varchar(max)
+varchar(n)
no se trunca (para varchar(max). -
varchar(max)
+nvarchar(n)
no se trunca (para nvarchar(max). -
nvarchar(max)
+varchar(n)
primero convertirá la entradavarchar(n)
anvarchar(n)
y luego hacer la concatenación. Si la longitud de la cadenavarchar(n)
es mayor que 4.000 caracteres, el cast seránvarchar(4000)
y se producirá el truncamiento.
Tipos de datos de literales de cadena
Si usa el prefijo N
y la cadena tiene nvarchar(n) donde n
es la longitud de la cadena. Así que N'Foo'
se tratará como nvarchar(3)
por ejemplo. Si la cadena es más larga que 4.000 caracteres se tratará como nvarchar(max)
Si no usa el prefijo N
y la cadena tiene varchar(n) donde n
es la longitud de la cadena. Si es más largo que varchar(max)
Para ambos de los anteriores si la longitud de la cadena es cero entonces n
se establece en 1.
Nuevos elementos de sintaxis.
1. La función CONCAT
no ayuda aquí
DECLARE @A5000 VARCHAR(5000) = REPLICATE('A',5000);
SELECT DATALENGTH(@A5000 + @A5000),
DATALENGTH(CONCAT(@A5000,@A5000));
Lo anterior devuelve 8000 para ambos métodos de concatenación.
2. Be cuidado con +=
DECLARE @A VARCHAR(MAX) = '';
SET @A+= REPLICATE('A',5000) + REPLICATE('A',5000)
DECLARE @B VARCHAR(MAX) = '';
SET @B = @B + REPLICATE('A',5000) + REPLICATE('A',5000)
SELECT DATALENGTH(@A),
DATALENGTH(@B);`
Devuelve
-------------------- --------------------
8000 10000
Note que @A
encontró truncamiento.
Cómo resolver el problema que estás experimentando.
Está obteniendo truncamiento ya sea porque está concatenando dos tipos de datos no max
juntos o porque está concatenando una cadena varchar(4001 - 8000)
a una cadena escrita nvarchar
(incluso nvarchar(max)
).
Para evitar el segundo problema, simplemente asegúrese de que todos los literales de cadena (o al menos aquellos con longitudes en el 4001 - 8000 range) están precedidos por N
.
Para evitar el primer problema, cambie la asignación de
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'Foo' + 'Bar' + ...;
A
DECLARE @SQL NVARCHAR(MAX) = '';
SET @SQL = @SQL + N'Foo' + N'Bar'
De modo que un NVARCHAR(MAX)
está involucrado en la concatenación desde el principio (como el resultado de cada concatenación también será NVARCHAR(MAX)
esto se propagará)
Evitar el truncamiento al ver
Asegúrese de que tiene seleccionado el modo" resultados a cuadrícula", entonces puede usar
select @SQL as [processing-instruction(x)] FOR XML PATH
Las opciones SSMS le permiten establecer una longitud ilimitada para XML
resultados. El bit processing-instruction
evita problemas con caracteres como <
que aparecen como <
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:31
Bien, así que si más adelante en la línea el problema es que tiene una consulta que es mayor que el tamaño permitido (que puede suceder si sigue creciendo) vas a tener que dividirla en trozos y ejecutar los valores de cadena. Entonces, digamos que tiene un procedimiento almacenado como el siguiente:
CREATE PROCEDURE ExecuteMyHugeQuery
@SQL VARCHAR(MAX) -- 2GB size limit as stated by Martin Smith
AS
BEGIN
-- Now, if the length is greater than some arbitrary value
-- Let's say 2000 for this example
-- Let's chunk it
-- Let's also assume we won't allow anything larger than 8000 total
DECLARE @len INT
SELECT @len = LEN(@SQL)
IF (@len > 8000)
BEGIN
RAISERROR ('The query cannot be larger than 8000 characters total.',
16,
1);
END
-- Let's declare our possible chunks
DECLARE @Chunk1 VARCHAR(2000),
@Chunk2 VARCHAR(2000),
@Chunk3 VARCHAR(2000),
@Chunk4 VARCHAR(2000)
SELECT @Chunk1 = '',
@Chunk2 = '',
@Chunk3 = '',
@Chunk4 = ''
IF (@len > 2000)
BEGIN
-- Let's set the right chunks
-- We already know we need two chunks so let's set the first
SELECT @Chunk1 = SUBSTRING(@SQL, 1, 2000)
-- Let's see if we need three chunks
IF (@len > 4000)
BEGIN
SELECT @Chunk2 = SUBSTRING(@SQL, 2001, 2000)
-- Let's see if we need four chunks
IF (@len > 6000)
BEGIN
SELECT @Chunk3 = SUBSTRING(@SQL, 4001, 2000)
SELECT @Chunk4 = SUBSTRING(@SQL, 6001, (@len - 6001))
END
ELSE
BEGIN
SELECT @Chunk3 = SUBSTRING(@SQL, 4001, (@len - 4001))
END
END
ELSE
BEGIN
SELECT @Chunk2 = SUBSTRING(@SQL, 2001, (@len - 2001))
END
END
-- Alright, now that we've broken it down, let's execute it
EXEC (@Chunk1 + @Chunk2 + @Chunk3 + @Chunk4)
END
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-28 12:41:51
Usted también usa nvarchar texto. eso significa que tienes que simplemente tenía una " N " antes de su cuerda masiva y eso es todo! ya no hay limitación
DELARE @SQL NVARCHAR(MAX);
SET @SQL = N'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-18 07:32:35
declare @p varbinary(max)
set @p = 0x
declare @local table (col text)
SELECT @p = @p + 0x3B + CONVERT(varbinary(100), Email)
FROM tbCarsList
where email <> ''
group by email
order by email
set @p = substring(@p, 2, 100000)
insert @local values(cast(@p as varchar(max)))
select DATALENGTH(col) as collen, col from @local
result collen > 8000, length col value is more than 8000 chars
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-23 09:35:54