Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Conversion Unicode vers Non-Unicode

Il y a quelques points à noter ici :

  1. Si vous voulez voir exactement quel caractère s'y trouve, vous pouvez convertir la valeur en VARBINARY qui vous donnera la valeur hexadécimale / binaire de tous les caractères de la chaîne et il n'y a pas de concept de caractères "cachés" en hexadécimal :

    DECLARE @PostalCode NVARCHAR(20);
    SET @PostalCode = N'053000'+ NCHAR(0x2008); -- 0x2008 = "Punctuation Space"
    SELECT @PostalCode AS [NVarCharValue],
           CONVERT(VARCHAR(20), @PostalCode) AS [VarCharValue],
           CONVERT(VARCHAR(20), RTRIM(@PostalCode)) AS [RTrimmedVarCharValue],
           CONVERT(VARBINARY(20), @PostalCode) AS [VarBinaryValue];
    

    Renvoie :

    NVarCharValue   VarCharValue   RTrimmedVarCharValue   VarBinaryValue
    053000          053000?        053000?                0x3000350033003000300030000820
    

    NVARCHAR les données sont stockées au format UTF-16 qui fonctionne dans des ensembles de 2 octets. En regardant les 4 derniers chiffres hexadécimaux pour voir quel est l'ensemble caché de 2 octets, nous voyons "0820". Étant donné que Windows et SQL Server sont UTF-16 Little Endian (c'est-à-dire UTF-16LE), les octets sont dans l'ordre inverse. Inverser les 2 derniers octets -- 08 et 20 -- nous obtenons "2008", qui est "l'espace de ponctuation" que nous avons ajouté via NCHAR(0x2008) .

    Veuillez également noter que RTRIM n'a pas aidé du tout ici.

  2. De manière simpliste, vous pouvez simplement remplacer les points d'interrogation par rien :

    SELECT REPLACE(CONVERT(VARCHAR(20), [PostalCode]), '?', '');
    
  3. Plus important encore, vous devez convertir le [PostalCode] champ à VARCHAR afin qu'il ne stocke pas ces caractères. Aucun pays n'utilise des lettres qui ne sont pas représentées dans le jeu de caractères ASCII et qui ne sont pas valides pour le type de données VARCHAR, du moins pour autant que j'en ai jamais lu (voir la section inférieure pour les références). En fait, ce qui est autorisé est un sous-ensemble plutôt petit d'ASCII, ce qui signifie que vous pouvez facilement filtrer en entrant (ou simplement faire le même REPLACE comme indiqué ci-dessus lors de l'insertion ou de la mise à jour) :

    ALTER TABLE [table] ALTER COLUMN [PostalCode] VARCHAR(20) [NOT]? NULL;
    

    Assurez-vous de vérifier le NULL actuel / NOT NULL paramètre pour la colonne et rendez-le identique dans l'instruction ALTER ci-dessus, sinon il pourrait être modifié car la valeur par défaut est NULL si non spécifié.

  4. Si vous ne pouvez pas modifier le schéma de la table et devez effectuer un "nettoyage" périodique des mauvaises données, vous pouvez exécuter ce qui suit :

    ;WITH cte AS
    (
       SELECT *
       FROM   TableName
       WHERE  [PostalCode] <>
                      CONVERT(NVARCHAR(50), CONVERT(VARCHAR(50), [PostalCode]))
    )
    UPDATE cte
    SET    cte.[PostalCode] = REPLACE(CONVERT(VARCHAR(50), [PostalCode]), '?', '');
    

    Veuillez garder à l'esprit que la requête ci-dessus n'est pas censée fonctionner efficacement si la table contient des millions de lignes. À ce stade, il devrait être géré dans des ensembles plus petits via une boucle.

Pour référence, voici l'article wikipedia pour Code postal , qui indique actuellement que les seuls caractères jamais utilisés sont :

Et concernant la taille max du champ, voici la Wikipedia Liste des codes postaux