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

Prise en charge d'UTF-8, SQL Server 2012 et l'UDT UTF8String

La création d'un type personnalisé défini par l'utilisateur via SQLCLR n'est pas , de quelque manière que ce soit, vous procurera un remplacement de n'importe quel type natif. C'est très pratique pour créer quelque chose pour gérer des données spécialisées. Mais les chaînes, même d'un encodage différent, sont loin d'être spécialisées. Suivre cette voie pour vos données de chaîne détruirait toute facilité d'utilisation de votre système, sans parler des performances, car vous ne seriez pas en mesure d'utiliser aucune fonctions de chaîne intégrées.

Si vous pouviez économiser quoi que ce soit sur l'espace disque, ces gains seraient effacés par ce que vous perdriez en performances globales. Le stockage d'un UDT se fait en le sérialisant dans un VARBINARY . Donc, pour faire n'importe quoi comparaison de chaînes OU tri, en dehors d'une comparaison "binaire" / "ordinale", vous devrez convertir toutes les autres valeurs, une par une, en UTF-8 pour ensuite effectuer la comparaison de chaînes qui peut tenir compte des différences linguistiques. Et cette conversion devrait être effectuée au sein de l'UDT. Cela signifie que, comme le type de données XML, vous devez créer l'UDT pour contenir une valeur particulière, puis exposer une méthode de cet UDT pour accepter un paramètre de chaîne pour effectuer la comparaison (c'est-à-dire Utf8String.Compare(alias.field1) ou, si vous définissez un opérateur pour le type, alors Utf8string1 = Utf8string2 et avoir le = l'opérateur récupère la chaîne dans l'encodage UTF-8, puis effectue la CompareInfo.Compare() ).

En plus des considérations ci-dessus, vous devez également tenir compte du fait que la transmission de valeurs via l'API SQLCLR a un coût, en particulier lors de l'utilisation de NVARCHAR(MAX) ou VARBINARY(MAX) par opposition à NVARCHAR(1 - 4000) et VARBINARY(1 - 4000) respectivement (veuillez ne pas confondre cette distinction avec l'utilisation de SqlChars / SqlBytes contre SqlString / SqlBinary ).

Enfin (au moins en termes d'utilisation d'un UDT), veuillez ne pas regarder au-delà du fait que l'UDT faisant l'objet de la demande est un exemple de code . Le seul test noté est purement fonctionnel, rien autour de l'évolutivité ou des "leçons apprises après avoir travaillé avec cela pendant un an". Le code de test fonctionnel est affiché ici sur la page CodePlex suivante et doit être examiné avant de prendre cette décision car il donne une idée de la façon dont vous auriez besoin d'écrire vos requêtes pour interagir avec (ce qui est bien pour un champ ou deux, mais pas pour la plupart/tous les champs de chaîne) :

http://msftengprodsamples.codeplex.com /SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql

Compte tenu du nombre de colonnes calculées persistantes et d'index ajoutés, l'espace a-t-il vraiment été économisé ?;-)

Lorsque l'espace (disque, mémoire, etc.) est le problème, vous avez trois options :

  1. Si vous utilisez SQL Server 2008 ou une version plus récente et que vous utilisez Enterprise Edition, vous pouvez activer Compression des données . La compression des données peut (mais pas "toujours") compresser les données Unicode dans NCHAR et NVARCHAR des champs. Les facteurs déterminants sont :

    1. NCHAR(1 - 4000) et NVARCHAR(1 - 4000) utilisez le Schéma de compression standard pour Unicode , mais uniquement à partir de SQL Server 2008 R2, ET uniquement pour les données IN ROW, pas OVERFLOW ! Cela semble être meilleur que l'algorithme de compression ROW/PAGE habituel.
    2. NVARCHAR(MAX) et XML (et je suppose aussi VARBINARY(MAX) , TEXT , et NTEXT ) les données IN ROW (pas hors ligne dans les pages LOB ou OVERFLOW) peuvent être au moins PAGE compressées, et peut-être également ROW compressé (pas sûr de ce dernier).
    3. Toutes les données OFF ROW, LOB ou OVERLOW =Aucune compression pour vous !
  2. Si vous utilisez une version antérieure à 2008 ou non sur Enterprise Edition, vous pouvez avoir deux champs :un VARCHAR et un NVARCHAR . Par exemple, disons que vous stockez des URL qui sont principalement tous des caractères ASCII de base (valeurs 0 - 127) et donc rentrent dans VARCHAR , mais comportent parfois des caractères Unicode. Votre schéma peut inclure les 3 champs suivants :

      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );
    

    Dans ce modèle, vous seulement SELECT à partir de [URL] colonne calculée. Pour l'insertion et la mise à jour, vous déterminez le champ à utiliser en voyant si la conversion modifie la valeur entrante, qui doit être de NVARCHAR saisissez :

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. Si vous avez des champs qui ne devraient jamais contenir que des caractères qui correspondent à une page de code particulière d'un jeu de caractères ASCII étendu, utilisez simplement VARCHAR .

PS Juste pour avoir ceci indiqué pour plus de clarté :le nouveau _SC Les classements introduits dans SQL Server 2012 permettent simplement :

  • les fonctions intégrées pour gérer correctement les caractères supplémentaires/paires de substitution, et
  • règles linguistiques pour les caractères supplémentaires utilisés pour le classement et les comparaisons

Mais, même sans le nouveau _SC Collations, vous pouvez toujours stocker n'importe quel caractère Unicode dans un XML ou N -type préfixé et récupérez-le sans perte de données. Cependant, lors de l'utilisation des anciens classements (c'est-à-dire sans numéro de version dans le nom), tous les caractères supplémentaires sont équivalents les uns aux autres. Vous devez utiliser le _90 et _100 Des classements qui vous permettent au moins d'obtenir des comparaisons et des tris binaires / points de code ; ils ne peuvent pas prendre en compte les règles linguistiques car ils n'ont pas de mappages particuliers des caractères supplémentaires (et n'ont donc pas de poids ou de règles de normalisation).

Essayez ce qui suit :

IF (N'𤪆' = N'𤪆') SELECT N'𤪆' AS [TheLiteral], NCHAR(150150) AS [Generated];
IF (N'𤪆' = N'𤪇') SELECT N'𤪇' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'𤪆' COLLATE Tatar_90_CI_AI = N'𤪇' COLLATE Tatar_90_CI_AI)
       SELECT N'𤪇 COLLATE Tatar_90_CI_AI' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'𤪆' = N'?') SELECT N'?';

Dans une base de données ayant un classement par défaut se terminant par _SC , seul le premier IF renverra un ensemble de résultats et le champ "Généré" affichera les caractères correctement.

Mais, si la base de données n'a pas de classement par défaut se terminant par _SC , et le classement n'est pas un _90 ou _100 classement des séries, puis les deux premiers IF les instructions renvoient des ensembles de résultats dans lesquels le champ "Généré" renverra NULL , et le champ "Literal" s'affiche correctement.

Pour les données Unicode, le classement n'a aucune incidence sur le stockage physique.

MISE À JOUR 2018-10-02

Bien que ce ne soit pas encore une option viable, SQL Server 2019 introduit la prise en charge native de l'UTF-8 dans VARCHAR / CHAR Types de données. Il y a actuellement trop de bogues pour qu'il puisse être utilisé, mais s'ils sont corrigés, alors c'est une option pour certains scénarios. Veuillez consulter mon message, "Prise en charge native de l'UTF-8 dans SQL Server 2019 :sauveur ou faux prophète ? ", pour une analyse détaillée de cette nouvelle fonctionnalité.