J'ai hérité de certaines bases de données SQL Server. Il y a une table (je vais appeler "G"), avec environ 86,7 millions de lignes et 41 colonnes de large, à partir d'une base de données source (je vais appeler "Q") sur SQL Server 2014 Standard qui obtient ETL pour une base de données cible (je vais appeler "P") avec le même nom de table sur SQL Server 2008 R2 Standard.
c'est-à-dire [Q]. [G] ---> [P]. [G]
EDIT: 3/20/2017: Certaines personnes ont demandé si la table source est la SEULE source de la table cible. Oui, c'est la seule source. En ce qui concerne l'ETL, aucune transformation réelle ne se produit; il s'agit en fait d'une copie 1: 1 des données source. Par conséquent, il n'est pas prévu d'ajouter des sources supplémentaires à cette table cible.
Un peu plus de la moitié des colonnes de [Q]. [G] sont VARCHAR (tableau source):
- 13 des colonnes sont VARCHAR (80)
- 9 des colonnes sont VARCHAR (30)
- 2 des colonnes sont VARCHAR (8).
De même, les mêmes colonnes dans [P]. [G] sont NVARCHAR (table cible), avec le même nombre de colonnes avec les mêmes largeurs. (En d'autres termes, même longueur, mais NVARCHAR).
- 13 des colonnes sont NVARCHAR (80)
- 9 des colonnes sont NVARCHAR (30)
- 2 des colonnes sont NVARCHAR (8).
Ce n'est pas ma conception.
Je voudrais ALTER [P]. [G] (cible) les types de données des colonnes de NVARCHAR à VARCHAR. Je veux le faire en toute sécurité (sans perte de données de conversion).
Comment puis-je consulter les valeurs de données dans chaque colonne NVARCHAR de la table cible pour confirmer si la colonne contient réellement des données Unicode?
Une requête (DMV?) Qui peut vérifier chaque valeur (dans une boucle?) De chaque colonne NVARCHAR et me dire si TOUTES les valeurs est authentique Unicode serait la solution idéale, mais d'autres méthodes sont les bienvenues.
la source
[G]
sont ETLed sur[P]
. Si[G]
c'est le casvarchar
, et que le processus ETL est le seul moyen par lequel les données entrent[P]
, alors à moins que le processus ajoute de vrais caractères Unicode, il ne devrait pas y en avoir. Si d'autres processus ajoutent ou modifient des données[P]
, vous devez être plus prudent - ce n'est pas parce que toutes les données actuelles peuvent l'êtrevarchar
que lesnvarchar
données ne pourront pas être ajoutées demain. De même, il est possible que tout ce qui consomme les données en ait[P]
besoinnvarchar
.Réponses:
Supposons qu'une de vos colonnes ne contienne aucune donnée Unicode. Pour vérifier que vous devez lire la valeur de colonne pour chaque ligne. Sauf si vous avez un index sur la colonne, avec une table rowstore, vous devrez lire chaque page de données de la table. Dans cet esprit, je pense qu'il est très logique de combiner toutes les vérifications de colonne en une seule requête sur la table. De cette façon, vous ne lirez pas les données de la table plusieurs fois et vous n'aurez pas à coder un curseur ou un autre type de boucle.
Pour vérifier une seule colonne, pensez que vous pouvez simplement faire ceci:
Un cast de
NVARCHAR
àVARCHAR
devrait vous donner le même résultat sauf s'il y a des caractères Unicode. Les caractères Unicode seront convertis en?
. Le code ci-dessus doit donc gérerNULL
correctement les cas. Vous avez 24 colonnes à vérifier, vous vérifiez donc chaque colonne dans une seule requête en utilisant des agrégats scalaires. Une implémentation est ci-dessous:Pour chaque colonne, vous obtiendrez un résultat
1
si l'une de ses valeurs contient unicode. Le résultat0
signifie que toutes les données peuvent être converties en toute sécurité.Je recommande fortement de faire une copie du tableau avec les nouvelles définitions de colonne et d'y copier vos données. Vous effectuerez des conversions coûteuses si vous le faites sur place, donc faire une copie pourrait ne pas être beaucoup plus lent. Avoir une copie signifie que vous pouvez facilement valider que toutes les données sont toujours là (une façon consiste à utiliser le mot-clé EXCEPT ) et vous pouvez annuler l'opération très facilement.
En outre, sachez que vous ne disposez peut-être pas de données Unicode actuellement, il est possible qu'un futur ETL puisse charger Unicode dans une colonne précédemment propre. S'il n'y a pas de vérification pour cela dans votre processus ETL, vous devriez envisager d'ajouter cela avant de faire cette conversion.
la source
NVARCHAR
colonneNVARCHAR
car elle est déjà de ce type. Et vous ne savez pas comment vous avez déterminé le caractère non convertible, mais vous pouvez convertir la colonneVARBINARY
pour obtenir les séquences d'octets UTF-16. Et UTF-16 est l'ordre des octets inversés, doncp
=0x7000
, puis vous inversez ces deux octets pour obtenir le point de codeU+0070
. Mais, si la source est VARCHAR, il ne peut pas s'agir d'un caractère Unicode. Il se passe autre chose. Besoin de plus d'informations.VARCHAR
sera implicitement converti enNVARCHAR
, mais il pourrait être préférable de le faireCONVERT(NVARCHAR(80), CONVERT(VARCHAR(80), column)) <> column
.SUBSTRING
fonctionne parfois, mais cela ne fonctionne pas avec les caractères supplémentaires lors de l'utilisation de classements qui ne se terminent pas_SC
, et celui que John utilise ne fonctionne pas, mais ce n'est probablement pas un problème ici. Mais la conversion en VARBINARY fonctionne toujours. EtCONVERT(VARCHAR(10), CONVERT(NVARCHAR(10), '›'))
ne se traduit pas par?
, donc je voudrais voir les octets. Le processus ETL a pu le convertir.Avant de faire quoi que ce soit, veuillez considérer les questions posées par @RDFozz dans un commentaire sur la question, à savoir:
Si la réponse est autre que "Je suis sûr à 100% qu'il s'agit de la seule source de données pour cette table de destination", n'apportez aucune modification, que les données actuellement dans la table puissent ou non être converties sans perte de données.
Et je voudrais ajouter une question connexe: Y at - il eu des discussions autour de plusieurs langues dans le soutien de la table source de courant (ie
[Q].[G]
) en convertissant ce àNVARCHAR
?Vous devrez demander autour de vous pour avoir une idée de ces possibilités. Je suppose qu'on ne vous a actuellement rien dit qui pourrait aller dans cette direction, sinon vous ne poseriez pas cette question, mais si ces questions ont été supposées être "non", alors elles doivent être posées et un public suffisamment large pour obtenir la réponse la plus précise / complète.
Le problème principal ici n'est pas tant d'avoir des points de code Unicode qui ne peuvent pas convertir (jamais), mais plus encore d'avoir des points de code qui ne tiennent pas tous sur une seule page de code. C'est la bonne chose à propos d'Unicode: il peut contenir des caractères de TOUTES les pages de codes. Si vous effectuez une conversion de
NVARCHAR
- où vous n'avez pas à vous soucier des pages de codes - versVARCHAR
, vous devrez vous assurer que le classement de la colonne de destination utilise la même page de codes que la colonne source. Cela suppose d'avoir une ou plusieurs sources utilisant la même page de codes (pas nécessairement le même classement, cependant). Mais s'il existe plusieurs sources avec plusieurs pages de codes, vous pouvez potentiellement rencontrer le problème suivant:Renvoie (2e jeu de résultats):
Comme vous pouvez le voir, tous ces caractères peuvent être convertis en
VARCHAR
, mais pas dans la mêmeVARCHAR
colonne.Utilisez la requête suivante pour déterminer la page de codes pour chaque colonne de votre table source:
CELA ÉTANT DIT....
Vous avez mentionné être sur SQL Server 2008 R2, MAIS, vous n'avez pas dit quelle édition. SI vous êtes sur Enterprise Edition, alors oubliez tout ce truc de conversion (puisque vous le faites probablement juste pour économiser de l'espace), et activez la compression des données:
Implémentation de la compression Unicode
Si vous utilisez Standard Edition (et il semble maintenant que vous l'êtes), il existe une autre possibilité à très long terme: la mise à niveau vers SQL Server 2016 puisque SP1 inclut la possibilité pour toutes les éditions d'utiliser la compression de données (rappelez-vous, j'ai dit "à long terme" "😉).
Bien sûr, maintenant qu'il vient d'être clarifié qu'il n'y a qu'une seule source pour les données, alors vous n'avez rien à craindre car la source ne peut pas contenir de caractères Unicode uniquement, ou des caractères en dehors de son code spécifique page. Dans ce cas, la seule chose à laquelle vous devez faire attention est d'utiliser le même classement que la colonne source, ou au moins un qui utilise la même page de codes. Cela signifie que si la colonne source utilise
SQL_Latin1_General_CP1_CI_AS
, vous pouvez utiliserLatin1_General_100_CI_AS
à la destination.Une fois que vous savez quel classement utiliser, vous pouvez soit:
ALTER TABLE ... ALTER COLUMN ...
êtreVARCHAR
(assurez-vous de spécifier le paramètreNULL
/ actuelNOT NULL
), ce qui nécessite un peu de temps et beaucoup d'espace de journal des transactions pour 87 millions de lignes, OUCréez de nouvelles colonnes "ColumnName_tmp" pour chacune et remplissez lentement via
UPDATE
doTOP (1000) ... WHERE new_column IS NULL
. Une fois que toutes les lignes sont remplies (et validées qu'elles ont toutes été copiées correctement! Vous pourriez avoir besoin d'un déclencheur pour gérer les MISES À JOUR, s'il y en a), dans une transaction explicite, utilisezsp_rename
pour permuter les noms des colonnes des colonnes "actuelles" à " _Old "puis les nouvelles colonnes" _tmp "pour supprimer simplement" _tmp "des noms. Appelez ensuitesp_reconfigure
la table pour invalider tous les plans mis en cache référençant la table, et s'il existe des vues référençant la table, vous devrez appelersp_refreshview
(ou quelque chose comme ça). Une fois que vous avez validé l'application et qu'ETL fonctionne correctement, vous pouvez supprimer les colonnes.la source
Latin1_General_100_CI_AS
est bien meilleur que celui que vous utilisez. Cela signifie que le comportement de tri et de comparaison sera le même entre eux, même s'il n'est pas aussi bon que le nouveau classement que je viens de mentionner.J'ai une certaine expérience avec cela de l'arrière quand j'avais un vrai travail. Comme à l'époque je voulais conserver les données de base, et que je devais également tenir compte des nouvelles données qui pourraient éventuellement avoir des caractères qui seraient perdus dans le shuffle, je suis allé avec une colonne calculée non persistante.
Voici un exemple rapide utilisant une copie de la base de données Super User du vidage de données SO .
Nous pouvons voir dès le départ qu'il existe des DisplayNames avec des caractères Unicode:
Ajoutons donc une colonne calculée pour déterminer combien! La colonne DisplayName est
NVARCHAR(40)
.Le compte renvoie ~ 3000 lignes
Cependant, le plan d'exécution est un peu compliqué. La requête se termine rapidement, mais cet ensemble de données n'est pas terriblement volumineux.
Comme les colonnes calculées n'ont pas besoin d'être persistées pour ajouter un index, nous pouvons effectuer l'une des actions suivantes:
Ce qui nous donne un plan un peu plus ordonné:
Je comprends que ce n'est pas la réponse, car cela implique des modifications architecturales, mais compte tenu de la taille des données, vous envisagez probablement d'ajouter des index pour faire face aux requêtes qui se joignent de toute façon à la table.
J'espère que cela t'aides!
la source
En utilisant l'exemple dans Comment vérifier si un champ contient des données Unicode , vous pouvez lire les données dans chaque colonne et faire le
CAST
et vérifier ci-dessous:la source