web-dev-qa-db-fra.com

SQL Server tronque silencieusement varchar dans les procédures stockées

Selon cette discussion de forum , SQL Server (j'utilise 2005 mais, si je comprends bien, cela vaut également pour 2000 et 2008) tronque silencieusement toute varchars que vous spécifiez en tant que paramètres de procédure stockée à la longueur du varchar, même si vous insérez cette Une chaîne directement à l'aide de INSERT provoquerait en réalité une erreur. par exemple. Si je crée cette table:

CREATE TABLE testTable(
    [testStringField] [nvarchar](5) NOT NULL
)

alors quand j'exécute ce qui suit:

INSERT INTO testTable(testStringField) VALUES(N'string which is too long')

Je reçois une erreur:

String or binary data would be truncated.
The statement has been terminated.

Génial. L'intégrité des données est préservée et l'appelant le sait. Définissons maintenant une procédure stockée pour insérer ceci:

CREATE PROCEDURE spTestTableInsert
    @testStringField [nvarchar](5)
AS
    INSERT INTO testTable(testStringField) VALUES(@testStringField)
GO

et l'exécuter:

EXEC spTestTableInsert @testStringField = N'string which is too long'

Aucune erreur, 1 ligne affectée. Une ligne est insérée dans la table, avec testStringField comme 'strin'. SQL Server a tronqué de manière silencieuse le paramètre varchar de la procédure stockée.

Maintenant, ce comportement peut être pratique à certains moments, mais je suppose qu’il n’ya AUCUN MOYEN de le désactiver. Ceci est extrêmement gênant, car je veux la chose à l'erreur si je passe une chaîne trop longue à la procédure stockée. Il semble y avoir 2 façons de gérer cela.

Commencez par déclarer le paramètre @testStringField du processus enregistré en tant que taille 6 et vérifiez si sa longueur est supérieure à 5. Cela ressemble à un piratage et implique des quantités irritantes de code passe-partout.

Deuxièmement, déclarez TOUS les paramètres varchar de la procédure stockée à varchar(max), puis laissez l’instruction INSERT de la procédure stockée échouer.

Ce dernier semble bien fonctionner, alors ma question est: est-ce une bonne idée d'utiliser varchar(max) ALWAYS pour les chaînes dans les procédures stockées SQL Server, si je veux réellement que le processus stocké échoue si une chaîne trop longue est passée? Pourrait-il même être la meilleure pratique? La troncature silencieuse qui ne peut pas être désactivée me semble stupide.

65
Jez

C'est juste est .

Je n'ai toutefois jamais remarqué de problème, car l'une de mes vérifications serait de m'assurer que mes paramètres correspondent à la longueur de mes colonnes. Dans le code client aussi. Personnellement, je m'attendrais à ce que SQL ne voie jamais des données trop longues. Si je voyais des données tronquées, la cause en serait saignée.

Si vous ressentez le besoin de varchar (max), évitez les problèmes de performances importants dus à priorité du type de données . varchar (max) a une priorité plus élevée que varchar (n) (le plus long est le plus élevé). Ainsi, dans ce type de requête, vous obtiendrez une analyse et non une recherche et chaque valeur varchar (100) sera exprimée en CAST en varchar (max).

UPDATE ...WHERE varchar100column = @varcharmaxvalue

Modifier:

Il existe un élément ouvert Microsoft Connect concernant ce problème.

Et il vaut probablement la peine d'être inclus dans paramètres Strict d'Erland Sommarkog (et élément Connect correspondant ).

Edit 2, après le commentaire de Martins:

DECLARE @sql VARCHAR(MAX), @nsql nVARCHAR(MAX);
SELECT @sql = 'B', @nsql = 'B'; 
SELECT 
   LEN(@sql), 
   LEN(@nsql), 
   DATALENGTH(@sql), 
   DATALENGTH(@nsql)
;

DECLARE @t table(c varchar(8000));
INSERT INTO @t values (replicate('A', 7500));

SELECT LEN(c) from @t;
SELECT 
   LEN(@sql + c), 
   LEN(@nsql + c), 
   DATALENGTH(@sql + c), 
   DATALENGTH(@nsql + c) 
FROM @t;
29
gbn

Comme toujours, merci à StackOverflow d’avoir suscité ce genre de discussion en profondeur. J'ai récemment exploré mes procédures stockées pour les rendre plus robustes en utilisant une approche standard des transactions et des blocs try/catch. Je ne partage pas l’avis de Joe Stefanelli selon lequel "Ma suggestion serait de responsabiliser le côté de l’application", et je suis tout à fait d’accord avec Jez: "Demander à SQL Server de vérifier que la longueur de la chaîne serait bien préférable". Pour moi, l’utilisation de procédures stockées tient au fait qu’elles sont écrites dans une langue native de la base de données et qu’elles doivent constituer une dernière ligne de défense. Du côté des applications, la différence entre 255 et 256 n’est qu’un chiffre sans signification, mais dans l’environnement de la base de données, un champ d’une taille maximale de 255 n’acceptera tout simplement pas 256 caractères. Les mécanismes de validation de l'application doivent refléter au mieux la base de données principale, mais la maintenance est difficile, je souhaite donc que la base de données me donne un bon retour d'informations si l'application autorise par erreur des données inappropriées. C'est pourquoi j'utilise une base de données au lieu d'un ensemble de fichiers texte au format CSV, JSON ou autre.

Je me demandais pourquoi l’un de mes SP avait jeté l’erreur 8152 et un autre tronqué en silence. J'ai finalement twigged: Le SP qui a renvoyé l'erreur 8152 avait un paramètre autorisant un caractère de plus que la colonne de la table associée. La colonne de la table était définie sur nvarchar (255) mais le paramètre était nvarchar (256). Donc, mon "erreur" ne répondrait-elle pas à la préoccupation de gbn: "problème de performances massif"? Au lieu d'utiliser max, nous pourrions peut-être systématiquement définir la taille de la colonne de la table sur, par exemple, 255 et le paramètre SP sur un seul caractère de plus, par exemple 256. Cela résout le problème de la troncature silencieuse et n'entraîne aucune perte de performances . Vraisemblablement, il y a un autre inconvénient auquel je n'ai pas pensé, mais cela me semble un bon compromis.

Mise à jour: Je crains que cette technique ne soit pas cohérente. Des tests supplémentaires révèlent que je peux parfois déclencher l'erreur 8152 et parfois les données sont tronquées de manière silencieuse. Je serais très reconnaissant si quelqu'un pouvait m'aider à trouver un moyen plus fiable de gérer cela.

Mise à jour 2: .__ Veuillez consulter la réponse de Pyitoechito sur cette page.

15
DavidHyogo

Le même comportement peut être vu ici:

declare @testStringField [nvarchar](5)
set @testStringField = N'string which is too long'
select @testStringField

Ma suggestion serait de charger le côté application de valider l'entrée avant d'appeler la procédure stockée.

4
Joe Stefanelli

Mise à jour: Je crains que cette technique ne soit pas cohérente. Des tests supplémentaires révèlent que je peux parfois déclencher l'erreur 8152 et parfois les données sont tronquées de manière silencieuse. Je serais très reconnaissant si quelqu'un pouvait m'aider à trouver un moyen plus fiable de gérer cela.

Cela est probablement dû au fait que le 256ème caractère de la chaîne est un espace blanc. VARCHARs tronquera l’espace blanc de fin lors de l’insertion et générera simplement un avertissement. Ainsi, votre procédure stockée tronque silencieusement vos chaînes à 256 caractères et votre insertion tronque l'espace blanc de fin (avec un avertissement). Cela produira une erreur lorsque ledit caractère n'est pas un espace blanc.

Une solution serait peut-être de faire en sorte que la procédure stockée VARCHAR soit une longueur appropriée pour intercepter un caractère ne contenant pas d'espace blanc. VARCHAR(512) serait probablement assez sûr.

4
Jenius

Une solution serait de:

  1. Modifier tous les paramètres entrants pour qu'ils soient varchar(max)
  2. Avoir sp variable privée de la longueur de donnée correcte (il suffit de copier et coller tous les paramètres et d’ajouter "int" à la fin
  3. Déclarer une variable de table avec les noms de colonne identiques aux noms de variable
  4. Insérer dans la table une ligne où chaque variable va dans la colonne du même nom
  5. Sélectionner dans le tableau en variables internes

De cette façon, vos modifications du code existant seront très minimes, comme dans l'exemple ci-dessous.

C'est le code original:

create procedure spTest
(
    @p1 varchar(2),
    @p2 varchar(3)
)

C'est le nouveau code:

create procedure spTest
(
    @p1 varchar(max),
    @p2 varchar(max)
)
declare @p1Int varchar(2), @p2Int varchar(3)
declare @test table (p1 varchar(2), p2 varchar(3)
insert into @test (p1,p2) varlues (@p1, @p2)
select @p1Int=p1, @p2Int=p2 from @test

Notez que si la longueur des paramètres entrants est supérieure à la limite au lieu de couper en mode silencieux la chaîne, SQL Server lève l'erreur .

1
igorp

Vous pouvez toujours lancer une instruction if dans votre sp qui en vérifie la longueur et, si elles sont supérieures à la longueur spécifiée, une erreur. Cela prend cependant beaucoup de temps et serait difficile à mettre à jour si vous mettez à jour la taille des données.

0
DForck42