Création d'un index sur un champ calculé: une chaîne ou des données binaires seraient tronquées

8

J'ai une table Fooavec les champs suivants:

ID bigint not null identity(1,1),
SerializedValue nvarchar(max),
LongValue as TRY_CAST(SerializedValue as bigint)

Maintenant, je veux créer un index sur LongValue, afin que je puisse facilement rechercher des valeurs sérialisées qui représentent des nombres.

create nonclustered index IX_Foo on Foo(LongValue);

Ce qui me fait l'erreur suivante:

Chaîne ou des données binaires seront tronquées.

Oui, il existe des données dans SerializedValue. Mais qu'est-ce qui, je vous prie, peut être tronqué en créant un index sur un champ calculé?

Shaul Behr
la source

Réponses:

8

L'erreur n'est pas causée par la création de l'index. L'erreur est due au fait TRY_CASTque les valeurs de colonne calculées sont évaluées lors de la création de l'index.

Si je lance ceci:

SELECT TRY_CAST(REPLICATE(CONVERT(nvarchar(MAX), N'a'), 4001) AS bigint)

J'ai la même erreur.

La documentation dit (soulignement le mien):

Si le transtypage réussit, TRY_CAST renvoie la valeur en tant que type_données spécifié; si une erreur se produit, null est renvoyé. Cependant, si vous demandez une conversion qui n'est explicitement pas autorisée, alors TRY_CAST échoue avec une erreur.

Maintenant, ce n'est pas exactement clair dans quels cas il échouera avec une erreur (semble un peu idiot étant donné tout le point de la fonction, mais de toute façon ...), nous pouvons donc corriger le code en transformant les valeurs d'entrée (utilisez quelque chose raisonnable pour les données de votre table), car il n'est pas nécessaire de traiter une énorme chaîne quand elle ne rentre pas dans un bigint de toute façon:

SELECT TRY_CAST(LEFT(REPLICATE(CONVERT(nvarchar(MAX), N'1'), 4001), 100) AS bigint)

Cela revient NULLcar la valeur n'est pas valide, mais elle ne bombarde pas avec une erreur.

Jon Seigel
la source
-1

Si vous avez une chaîne avec une valeur trop longue, la création de l'index échouera. J'ai essayé un petit code de test en utilisant SQL Server 2012.

CREATE TABLE dbo.foo 
(ID bigint not null identity(1,1),
SerializedValue nvarchar(max),
LongValue as TRY_CAST(SerializedValue as bigint));

INSERT INTO dbo.foo (serializedvalue) VALUES(REPLICATE(' ', 4000)+'1');

CREATE INDEX GotToTry ON foo(LongValue);

DROP TABLE dbo.foo;
GO

Mon expérience rapide a montré que le code fonctionne tant que la valeur nvarchar (max) est de 4000 caractères ou moins. (Bien sûr, tous les blancs sans rien à la fin se réduisent à aucun caractère et fonctionnent donc très bien.) Le 4001e caractère déclenche le String or binary data would be truncatedmessage. Vous pouvez donc examiner vos données pour une valeur de série qui dépasse 4 000 caractères.

EDIT: Oui, la conversion est en a BIGINT. Le problème n'est pas le BIGINT, mais le NVARCHAR(MAX). Par exemple:

  1. Si une ligne contient «111111111111111111111», elle CREATE INDEXconvertira les deux en valeur BIGINT.
  2. Si une ligne est comprise entre 0 et 4000 '1, c'est possible CREATE INDEX, mais la valeur peut l'être NULLcar elle déborde BIGINT.
  3. Si une ligne contient plus de 4 000 caractères, le CREATE INDEXéchoue.

Il semble donc que le contenu réel de NVARCHAR (MAX) soit ce qui compte pour CREATE INDEX.

EDIT: Jon Seigel a identifié que TRY_CAST déclenche l'échec lors de la création de l'index lorsque la chaîne est plus longue que nvarchar (4000).

RLF
la source
2
Cela ne répond pas vraiment à la question. L'index est sur un bigint. Ça ne sera jamais autre chose qu'un gros morceau. La question est de savoir pourquoi les données seraient tronquées quand un bigint est bien dans la limite de taille d'un indice
Mark Sinkinson
1
@MarkSinkinson Modifié pour fournir plus de détails. Le problème est le contenu de NVARCHAR (MAX).
RLF