Tous, j'ai une grande requête SQL dynamique (inévitable). En raison du nombre de champs dans les critères de sélection, la chaîne contenant le code SQL dynamique dépasse 4 000 caractères. Je comprends maintenant qu’il existe un maximum de 4 000 défini pour NVARCHAR(MAX)
, mais en regardant le SQL exécuté dans Profiler Server pour l’instruction
DELARE @SQL NVARCHAR(MAX);
SET @SQL = 'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
Cela semble fonctionner (!?). Pour une autre requête également volumineuse, une erreur associée à cette limite de 4000 (!?) est supprimée. Elle supprime tout le SQL après cette limite de 4000 et me laisse avec une erreur de syntaxe. Malgré cela, l'éditeur de profil affiche cette requête SQL dynamique dans full (!?).
Que se passe-t-il exactement ici et devrais-je simplement convertir cette variable @SQL en VARCHAR et passer à autre chose?
Merci pour votre temps.
Ps. Il serait également agréable de pouvoir imprimer plus de 4000 caractères pour examiner ces grandes requêtes. Les suivants sont limités à 4000
SELECT CONVERT(XML, @SQL);
PRINT(@SQL);
y a-t-il une autre manière cool?
Je comprends qu’un maximum de 4000 est défini pour
NVARCHAR(MAX)
Votre compréhension est fausse. nvarchar(max)
peut stocker jusqu'à (et au-delà parfois) 2 Go de données (1 milliard de caractères double octet).
De nchar et nvarchar dans Livres en ligne, la grammaire est
nvarchar [ ( n | max ) ]
Le caractère |
signifie qu'il s'agit d'alternatives. c'est-à-dire que vous spécifiez soitn
ou le littéral max
.
Si vous choisissez de spécifier une n
spécifique, celle-ci doit être comprise entre 1 et 4 000, mais utiliser max
le définit comme un type de données d'objet volumineux (en remplacement de ntext
qui est obsolète).
En fait, dans SQL Server 2008, il semble que pour variable, la limite de 2 Go puisse être dépassée indéfiniment, sous réserve d'un espace suffisant dans tempdb
( affiché ici )
En ce qui concerne les autres parties de votre question
varchar(n) + varchar(n)
tronquera à 8 000 caractères. nvarchar(n) + nvarchar(n)
tronquera à 4 000 caractères. varchar(n) + nvarchar(n)
tronquera à 4 000 caractères. nvarchar
a une priorité plus élevée donc le résultat est nvarchar(4,000)
[n]varchar(max)
+ [n]varchar(max)
ne sera pas tronqué (pour <2 Go). varchar(max)
+ varchar(n)
ne tronquera pas (pour <2 Go) et le résultat sera saisi sous la forme varchar(max)
.varchar(max)
+ nvarchar(n)
ne tronquera pas (pour <2 Go) et le résultat sera saisi sous la forme nvarchar(max)
. nvarchar(max)
+ varchar(n)
convertira d'abord l'entrée varchar(n)
en nvarchar(n)
, puis effectuera la concaténation. Si la longueur de la chaîne varchar(n)
est supérieure à 4 000 caractères, la conversion sera en nvarchar(4000)
et la troncature se produira.Si vous utilisez le préfixe N
et que la chaîne a une longueur <= 4 000 caractères, le nom sera nvarchar(n)
où n
correspond à la longueur de la chaîne. Donc, N'Foo'
sera traité comme nvarchar(3)
par exemple. Si la chaîne compte plus de 4 000 caractères, elle sera traitée comme nvarchar(max)
Si vous n'utilisez pas le préfixe N
et que la chaîne contient <= 8 000 caractères, il sera saisi sous la forme varchar(n)
où n
correspond à la longueur de la chaîne. Si plus longtemps que varchar(max)
Dans les deux cas précédents, si la longueur de la chaîne est égale à zéro, n
est défini sur 1.
1. La fonction CONCAT
n'aide pas ici
DECLARE @A5000 VARCHAR(5000) = REPLICATE('A',5000);
SELECT DATALENGTH(@A5000 + @A5000),
DATALENGTH(CONCAT(@A5000,@A5000));
Ce qui précède renvoie 8000 pour les deux méthodes de concaténation.
2. Faites attention avec +=
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);`
Résultats
-------------------- --------------------
8000 10000
Notez que @A
a rencontré une troncature.
Vous obtenez une troncature soit parce que vous concaténez deux types de données non max
ensemble, soit parce que vous concaténez une chaîne varchar(4001 - 8000)
en une chaîne nvarchar
(même nvarchar(max)
).
Pour éviter le second problème, assurez-vous simplement que tous les littéraux de chaîne (ou du moins ceux dont la longueur est comprise entre 4001 et 8000) sont précédés de N
.
Pour éviter le premier problème, changez l’affectation de
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'Foo' + 'Bar' + ...;
À
DECLARE @SQL NVARCHAR(MAX) = '';
SET @SQL = @SQL + N'Foo' + N'Bar'
de sorte qu'un NVARCHAR(MAX)
soit impliqué dans la concaténation depuis le début (car le résultat de chaque concaténation sera également NVARCHAR(MAX)
cela se propagera)
Assurez-vous que le mode "résultats sur la grille" est sélectionné, puis vous pouvez utiliser
select @SQL as [processing-instruction(x)] FOR XML PATH
Les options SSMS vous permettent de définir une longueur illimitée pour les résultats XML
. Le bit processing-instruction
évite les problèmes avec des caractères tels que <
qui apparaît sous la forme <
.
OK, donc si plus tard dans la ligne le problème est que vous avez une requête supérieure à la taille autorisée (ce qui peut arriver si elle continue de croître), vous devrez la diviser en morceaux et exécuter le valeurs de chaîne. Donc, supposons que vous ayez une procédure stockée comme celle-ci:
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
Vous devez également utiliser le texte nvarchar. cela signifie que vous devez simplement avoir un "N" avant votre chaîne massive et c'est tout! plus de limite
DELARE @SQL NVARCHAR(MAX);
SET @SQL = N'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
La réponse acceptée m'a aidée, mais j'ai été prise en défaut lors de la concaténation de varchars impliquant des déclarations de cas. Je sais que la question du PO ne concerne pas les déclarations de cas, mais j’ai pensé qu’il serait utile de poster ici pour des personnes comme moi qui se sont retrouvées ici tout en luttant pour créer de longues instructions SQL dynamiques impliquant des déclarations de cas.
Lors de l'utilisation d'instructions case avec concaténation de chaînes, les règles mentionnées dans la réponse acceptée s'appliquent à chaque section de l'instruction case indépendamment.
declare @l_sql varchar(max) = ''
set @l_sql = @l_sql +
case when 1=1 then
--without this correction the result is truncated
--CONVERT(VARCHAR(MAX), '')
+REPLICATE('1', 8000)
+REPLICATE('1', 8000)
end
print len(@l_sql)
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