web-dev-qa-db-fra.com

La conversion de données varbinary en varchar dans SQLServer conduit à des résultats inattendus

J'ai besoin de résoudre un problème de conversion de données dans SQL Server 2008. J'ai obtenu une modification des exigences sur le stockage des données. Sans grand soin, j'ai converti les données varbinary existantes en varchar en utilisant CONVERT(NVARCHAR(max), @bytearraydata, 1).

La même conversion en C # est effectuée à l'aide des méthodes Encoding.Default.GetString Et Encoding.Default.GetBytes. Encoding.Default.GetBytes(string) récupère le bytearray tel qu'il était auparavant. Mais quand j'essaye de récupérer le tableau d'octets de chaîne que j'ai converti en utilisant CONVERT() me donne un mauvais résultat.

Mon travail consiste à récupérer le tableau d'octets stocké sous forme de chaîne dans la base de données et à le convertir en tableau d'octets et enfin à rendre le contenu au format PDF. Les données passant par le mécanisme de codage (pendant la sauvegarde et la récupération) fonctionnent bien pour moi. Mais lorsque j'essaie de récupérer les données qui ont été converties à l'aide de CONVERT, il ne parvient pas à générer de PDF.

Comment puis-je résoudre ce problème?

Sommaire:

La colonne du tableau d'octets a été remplacée par une chaîne.

Conversion de données existante effectuée à l'aide de cette fonction:

Convert(NVARCHAR(MAX), @bytearraydata, 1)

Dans l'application, la conversion du tableau d'octets se fait à l'aide de Encoding.Default.GetString(bytearraydata)

Encoding et CONVERT ne sont-ils pas compatibles?

3
user3169103

Lorsque vous utilisez Encoding.Default le résultat dépend des paramètres locaux:

Un codage pour la page de codes ANSI actuelle du système d'exploitation.

La spécification est assez directe sur les dangers de l'utilisation de l'encodage par défaut et appelle très spécifiquement la recommandation de ne pas l'utiliser:

Différents ordinateurs peuvent utiliser différents encodages par défaut, et l'encodage par défaut peut même changer sur un seul ordinateur. Par conséquent, les données transmises en continu d'un ordinateur à un autre ou même récupérées à différents moments sur le même ordinateur peuvent être traduites incorrectement. En outre, le codage renvoyé par la propriété Default utilise la solution de secours la mieux adaptée pour mapper les caractères non pris en charge aux caractères pris en charge par la page de codes. Pour ces deux raisons, l'utilisation de l'encodage par défaut n'est généralement pas recommandée

Maintenant, pour une raison quelconque, vous vous attendez à ce que le codage local actuel aléatoire corresponde au codage du serveur. Même si la fonction CONVERT ferait ce que vous croyez, les résultats seraient aléatoires et imprévisibles car:

  • la page de codes locale du serveur peut différer de la page de codes du client.
  • un changement de page de code rendrait les données persistantes illisibles car elles ont été écrites avec un codage et ont ensuite tenté d'être lues avec un autre.

De plus, CONVERT ne fait pas ce que vous attendez. CONVERT convertira un VARBINARY en NVARCHAR en utilisant le codage UCS-2, car il s'agit du codage utilisé par SQL Server pour les données NVARCHAR.

Je vous suggère d'aborder votre problème urgent en lisant d'abord cet article Le minimum absolu que chaque développeur de logiciels doit absolument connaître positivement Unicode et les jeux de caractères (pas d'excuses!) . Suivi avec Considérations internationales pour SQL Server .

1
Remus Rusanu

Il y a quelques confusions dans la question, conduisant à des résultats inattendus:

  1. Les termes VARCHAR et NVARCHAR sont utilisés de manière interchangeable (du moins il semble), mais ils sont très différents. NVARCHAR est un encodage 16 bits - UTF-16 LE (Little Endian) pour être exact - et cela ne change pas. VARCHAR est un codage 8 bits, et le codage 8 bits spécifique utilisé est déterminé par la page de codes associée au classement de la colonne (nous ignorerons les données VARCHAR dans les littéraux de chaîne et pour l'instant puisque cette question concerne les données stockées dans une table). Si vous voulez savoir quelle page de code est associée à un classement particulier, vous pouvez utiliser les fonctions intégrées COLLATIONPROPERTY:

    SELECT COLLATIONPROPERTY(N'Latin1_General_100_CI_AS_SC', 'CodePage') AS [CodePage];
    -- 1252
    
  2. Lors de la conversion entre VARBINARY et VARCHAR ou NVARCHAR, vous devez faire attention à être cohérent avec ce type de données de chaîne. Vous ne pouvez pas convertir de VARCHAR en VARBINARY, puis prendre le même VARBINARY et le convertir en NVARCHAR.

  3. La classe Encoding dans .NET représente un encodage particulier du texte, qu'il soit 7 bits, 8 bits, 16 bits, 32 bits ou variable (comme UTF-8). Pour obtenir des résultats "attendus", vous devez créer un codage qui correspond à ce dont vous avez besoin de convertir vers ou depuis, en ce qui concerne la représentation byte[]. Les chaînes dans .NET sont toujours UTF-16 LE (comme NVARCHAR), et c'est à cela que fait référence le codage Unicode dans .NET. La représentation byte[] D'un encodage sera de n'importe quel encodage créé, mais la représentation sous forme de chaîne sera toujours UTF-16 LE. L'encodage à créer dépend donc du type de données que vous traitez:

    • NVARCHAR: Utilisez Encoding.Unicode
    • VARCHAR: Déterminez la page de codes du classement via COLLATIONPROPERTY(N'collation_name', 'CodePage'), puis utilisez cette valeur int dans Encoding.GetEncoding(CodePageIntValue).
  4. Lorsque vous utilisez la fonction intégrée CONVERT, faites attention au numéro de "style" que vous utilisez. Par exemple:

    SELECT CONVERT(VARBINARY(50), N'bob');
    -- 0x62006F006200
    

    Maintenant, prenez cette valeur renvoyée VARBINARY et convertissez-la en NVARCHAR, en utilisant des valeurs de "style" de 0 (par défaut) et 1 (qui est ce que votre fonction CONVERT utilise dans la question):

    SELECT CONVERT(NVARCHAR(MAX), 0x62006F006200, 0) AS [Style_0],
           CONVERT(NVARCHAR(MAX), 0x62006F006200, 1) AS [Style_1];
    

    Retour:

    Style_0        Style_1
    bob            0x62006F006200
    

Donc, si l'énoncé suivant de la question est vrai:

Dans l'application, la conversion du tableau d'octets se fait à l'aide de Encoding.Default.GetString(bytearraydata)

alors cela équivaudrait à utiliser VARCHAR au lieu de NVARCHAR, et une valeur de "style" de 0 (ou rien) au lieu de 1:

CONVERT(VARCHAR(MAX), 0x62006F006200)
1
Solomon Rutzky

Je ne peux pas reproduire ce problème. Des étapes supplémentaires ont-elles été impliquées? Je peux convertir du texte en binaire et vice versa, ou vice versa, sans perte:

DECLARE @OrigText      VARCHAR  (100) = 'There once was a bear'
DECLARE @Binary        VARBINARY(100) = CONVERT(VARBINARY(100), @OrigText)
DECLARE @RoundTripText VARCHAR  (100) = CONVERT(VARCHAR  (100), @Binary)
DECLARE @RoundTripBin  VARBINARY(100) = CONVERT(VARBINARY(100), @RoundTripText)

SELECT @OrigText, @Binary, @RoundTripText, @RoundTripBin

Résultats:

  • Il y avait une fois un ours
  • 0x5468657265206F6E63652077617320612062656172
  • Il y avait une fois un ours
  • 0x5468657265206F6E63652077617320612062656172

Cela fonctionne également avec NVARCHAR et en utilisant CAST plutôt que CONVERT. Notez que je ne spécifie pas de style pour CONVERT; si vous en spécifiez une, je crois comprendre que votre texte doit être une chaîne hexadécimale. Est-ce ce que vous stockez ou est-ce un texte plus conventionnel?

0
Jon of All Trades