web-dev-qa-db-fra.com

nvarchar (max) toujours tronqué

J'écris donc une procédure stockée dans MS SQL Server 2008. C'est une très longue requête et je dois l'écrire dynamiquement, donc je crée une variable appelée @Query Et je la fais de type NVARCHAR(MAX). Maintenant, j'ai été dit que dans les versions modernes de SQL Server, NVARCHAR(MAX) peut contenir une quantité ridicule de données, bien plus que le maximum de 4000 caractères d'origine. Cependant, @Query Est toujours tronqué à 4000 caractères lorsque j'essaie de l'imprimer.

DECLARE @Query NVARCHAR(max);
SET @Query = 'SELECT...' -- some of the query gets set here
SET @Query = @Query + '...' -- more query gets added on, etc.

-- later on...
PRINT LEN(@Query) -- Prints out 4273, which is correct as far as I can tell
PRINT @Query      -- Truncates value to 4000 characters
EXEC sp_executesql @Query -- totally crashes due to malformed (truncated) query

Suis-je en train de faire quelque chose de mal ou ai-je complètement tort sur la façon dont NVARCHAR(MAX) fonctionne?

49
Andrew Arnold

Pour voir le SQL dynamique généré, passez en mode texte (raccourci: Ctrl-T), puis utilisez SELECT

PRINT LEN(@Query) -- Prints out 4273, which is correct as far as I can tell
--SET NOCOUNT ON
SELECT @Query

Pour ce qui est de sp_executesql, essayez ceci (en mode texte), il devrait montrer les trois aaaaa... est le milieu le plus long avec 'SELECT ..' ajouté. Regarder le Ln... Col.. indicateur dans la barre d'état en bas à droite montrant 4510 à la fin de la 2ème sortie.

declare @n nvarchar(max)
set @n = REPLICATE(convert(nvarchar(max), 'a'), 4500)
SET @N = 'SELECT ''' + @n + ''''
print @n   -- up to 4000
select @n  -- up to max
exec sp_Executesql @n
12
RichardTheKiwi

Le problème semble être associé à l'instruction SET. Je pense que l'expression ne peut pas dépasser 4 000 octets. Il n'est pas nécessaire de modifier les paramètres si tout ce que vous essayez de faire est d'attribuer une instruction générée dynamiquement de plus de 4 000 caractères. Ce que vous devez faire est de diviser votre mission. Si votre instruction contient 6 000 caractères, recherchez un point d'arrêt logique, puis concaténez la seconde moitié à la même variable. Par exemple:

SET @Query = 'SELECT ....' [Up To 4,000 characters, then rest of statement as below]

SET @Query = @Query + [rest of statement]

Exécutez maintenant votre requête normalement, c'est-à-dire EXEC ( @Query )

59
kannas

Le problème vient de la conversion implicite.

Si vous avez des valeurs Unicode/nChar/nVarChar que vous concaténez, alors SQL Server convertira implicitement votre chaîne en nVarChar (4000), et il est malheureusement trop stupide de réaliser qu'il tronquera votre chaîne ou même vous avertira que des données ont été tronqué d'ailleurs!

Lors de la concaténation de chaînes longues (ou de chaînes qui selon vous pourraient être longues) toujours pré-concaténer votre chaîne de construction avec CAST ('' comme nVarChar (MAX)) comme ceci:

SET @Query = CAST('' as nVarChar(MAX))--Force implicit conversion to nVarChar(MAX)
           + 'SELECT...'-- some of the query gets set here
           + '...'-- more query gets added on, etc.

Quelle douleur et effrayant de penser que c'est juste comment SQL Server fonctionne. :(

Je sais que d'autres solutions de contournement sur le Web disent de diviser votre code en plusieurs affectations SET/SELECT en utilisant plusieurs variables, mais cela n'est pas nécessaire étant donné la solution ci-dessus.

Pour ceux qui ont atteint un maximum de 8000 caractères, c'était probablement parce que vous n'aviez pas d'Unicode donc il a été implicitement converti en VarChar (8000).

Explication:
. (8000) (selon ce que vous concaténez). Une fois qu'il a fini de déterminer la valeur (et après l'avoir tronquée pour vous), il la convertit en (MAX) lors de son affectation à votre variable, mais il est alors trop tard.

55
MikeTeeVee

Les résultats en texte n'autorisent qu'un maximum de 8 192 caractères.

Screenshot

J'utilise cette approche

DECLARE @Query NVARCHAR(max);

set @Query = REPLICATE('A',4000)
set @Query = @Query + REPLICATE('B',4000)
set @Query = @Query + REPLICATE('C',4000)
set @Query = @Query + REPLICATE('D',4000)

select LEN(@Query)

SELECT @Query /*Won't contain any "D"s*/
SELECT @Query as [processing-instruction(x)] FOR XML PATH /*Not truncated*/
5
Martin Smith

Votre premier problème est une limitation de l'instruction PRINT. Je ne sais pas pourquoi sp_executesql échoue. Il devrait prendre en charge à peu près n'importe quelle longueur d'entrée.

La raison pour laquelle la requête est mal formée est peut-être autre chose que la troncature.

4
Marcelo Cantos

Imprimer tronque le varchar (MAX) à 8000, nvarchar (MAX) à 4000 caractères.

Mais;

PRINT CAST(@query AS NTEXT)

imprimera la requête entière.

2
boyukbas

Utilisez ceci PRINT BIG fonction pour tout afficher:

IF OBJECT_ID('tempdb..#printBig') IS NOT NULL
  DROP PROCEDURE #printBig

GO

CREATE PROCEDURE #printBig (
   @text NVARCHAR(MAX)
 )
AS

--DECLARE @text NVARCHAR(MAX) = 'YourTextHere'
DECLARE @lineSep NVARCHAR(2) = CHAR(13) + CHAR(10)  -- Windows \r\n

DECLARE @off INT = 1
DECLARE @maxLen INT = 4000
DECLARE @len INT

WHILE @off < LEN(@text)
BEGIN

  SELECT @len =
    CASE
      WHEN LEN(@text) - @off - 1 <= @maxLen THEN LEN(@text)
      ELSE @maxLen
             - CHARINDEX(REVERSE(@lineSep),  REVERSE(SUBSTRING(@text, @off, @maxLen)))
             - LEN(@lineSep)
             + 1
    END
  PRINT SUBSTRING(@text, @off, @len)
  --PRINT '@off=' + CAST(@off AS VARCHAR) + ' @len=' + CAST(@len AS VARCHAR)
  SET @off += @len + LEN(@lineSep)

END

La source:

https://www.richardswinbank.net/doku.php?id=tsql:print_big

1
Paul Andrew

J'ai rencontré le même problème aujourd'hui et j'ai découvert qu'au-delà de cette limite de 4 000 caractères, je devais diviser la requête dynamique en deux chaînes et les concaténer lors de l'exécution de la requête.

DECLARE @Query NVARCHAR(max);
DECLARE @Query2 NVARCHAR(max);
SET @Query = 'SELECT...' -- some of the query gets set here
SET @Query2 = '...' -- more query gets added on, etc.

EXEC (@Query + @Query2)
1
Patrick

Le problème avec la création de SQL dynamique à l'aide d'une expression de chaîne est que SQL limite l'évaluation des expressions de chaîne à 4 000 caractères. Vous pouvez affecter une chaîne plus longue à une variable nvarchar (max), mais dès que vous incluez + dans l'expression (comme + CASE ... END +), le résultat de l'expression est limité à 4 000 caractères.

Une façon de résoudre ce problème consiste à utiliser CONCAT au lieu de +. Par exemple:

SET @sql = CONCAT(@sql, N'
     ... dynamic SQL statements ...
    ', CASE ... END, N'
     ... dynamic SQL statements ...
    ')

Où @sql est déclaré comme nvarchar (max).

1
Tony M