web-dev-qa-db-fra.com

Modifier le classement sur la colonne de partition (SQL Server 2017)

J'essaie de changer le classement sur la colonne:

ALTER TABLE [dbo].[yourdad] ALTER COLUMN [yourmam] varchar(36) COLLATE SQL_Latin1_General_CP1_CI_AI NOT NULL; 

et il donne un message comme celui-ci:

Msg 5074, niveau 16, état 1, ligne 1 L'objet 'yourdad' dépend de la colonne 'yourmam'. Msg 4922, niveau 16, état 9, ligne 1 ALTER TABLE ALTER COLUMN yourmam a échoué car un ou plusieurs objets accèdent à cette colonne.

Il a un schéma de partition et une fonction qui dépendent de cette colonne.

Actuellement, j'ai plus de 40k de table avec de telles colonnes où le classement doit être modifié.

Est-il possible de changer le classement sans recréation de table?

  1. Supprimez les index partitionnés - supprimés.

  2. Mais quand j'essaie de supprimer le schéma:

    SCHÉMA DE PARTITION DROP yourdad

Msg 7717, niveau 16, état 1, ligne 1 Le schéma de partition "yourdad" est actuellement utilisé pour partitionner une ou plusieurs tables.

C'est un nouveau classement de base de données insensible à la casse sur la base de données que je ne veux pas installer, l'ancien était sensible à la casse.

6
Mikhail Aksenov

Vous n'avez pas besoin de supprimer la ou les tables.

Une option (que j'ai testée) est:

  1. Supprimez les index non clusterisés partitionnés; déplacer les index clusterisés partitionnés, les clés primaires et les contraintes uniques vers un groupe de fichiers non partitionné; et des partitions de partition en créant un index cluster (temporaire) sur un groupe de fichiers non partitionné. Pour obtenir de l'aide pour identifier les index affectés et les actions à entreprendre pour chacun d'eux, veuillez consulter la requête que j'ai publiée dans la section "Recherche et correction des objets ignorés" de "Modification du classement de l'instance, les bases de données") et toutes les colonnes de toutes les bases de données utilisateur: qu'est-ce qui pourrait mal tourner? " (qui contient également des exemples de traitement des index, des contraintes et des segments groupés).
  2. Supprimer le ou les schémas de partition
  3. Supprimer la ou les fonctions de partition
  4. Modifier la définition de colonne avec le nouveau classement
  5. Supprimez et recréez la fonction de partition pour spécifier la clause COLLATE avec le nouveau classement pour le paramètre d'entrée (en supposant ici que vous êtes pas en changeant le classement par défaut de la base de données, et le paramètre d'entrée utilisera le classement par défaut de la base de données pour les types de chaîne, sauf indication contraire à l'aide de la clause COLLATE, et l'instruction CREATE INDEX utilisant ce schéma et cette fonction échouera si le classement de ce paramètre ne correspond pas à la classement de la colonne de partitionnement). Cela signifie que si vous changez le classement de la colonne en SQL_Latin1_General_CP1_CI_AI Mais que la base de données utilise toujours SQL_Latin1_General_CP1_CI_AS, Vous devez:

    CREATE PARTITION FUNCTION PartitionFunc
    (
      VARCHAR(36) COLLATE SQL_Latin1_General_CP1_CI_AI
    )
    ...
    

Maintenant, 40k + tables sont peut-être plus que quelques-unes, mais si elles utilisent toutes le même schéma de partition et la même fonction, il ne s'agit que de 4 instructions supplémentaires (supprimer et créer pour le schéma de partition et la fonction de partition) par rapport à ce que vous avez déjà besoin de faire même si le partitionnement n'était pas utilisé (c'est-à-dire que vous auriez besoin de supprimer et de recréer les index quoi qu'il arrive). Ainsi, bien que cela nécessite beaucoup d'E/S lorsque les index sont supprimés et recréés, il n'y a aucun moyen de contourner cela lors de la modification du classement d'une colonne.

Je suppose (et croisons les doigts) que ces colonnes sont pas:

  • partie de l'index clusterisé
  • des champs clés dans une clé primaire ou un index/contrainte unique dont la clé étrangère est

Si l'un de ces cas est vrai, vous avez encore beaucoup de travail à faire, mais cela n'a rien à voir avec le partitionnement.


Une autre option (étant donné le désir de pas tout recréer) pourrait être d'utiliser le non documenté sqlservr.exe -q option pour modifier le classement sur [~ # ~] all [~ # ~] colonnes utilisateur et bases de données sur l'instance. Cette approche modifie les métadonnées de la base de données, des colonnes, des paramètres, etc., puis reconstruit les index (là encore, aucun moyen de contourner cela lors de la modification des classements, donc avec 40k + tables, cela prendra probablement quelques minutes (ou plus) et le la base de données, ou l'instance entière si vous mettez à jour le classement globalement, sera hors ligne pendant cette période).

Je viens de tester ce scénario sur SQL Server 2019 RC1, et cela fonctionne pas pour les fonctions de partition, mais cela ne signifie pas que cette approche ne fait pas partie de la solution globale. Cela signifie simplement que vous n'avez vraiment pas d'autre choix que de suivre le processus indiqué dans la section ci-dessus. L'approche sqlservr.exe -q gérera tout le reste (sauf pour les types de table définis par l'utilisateur, je crois). Il y a beaucoup de restrictions sur la possibilité de modifier le classement d'une colonne, et encore plus sur le changement du classement par défaut de la base de données (beaucoup de dépendances dans les deux cas), donc si vous voulez avoir une base de données sensible à la casse maintenant, alors vous le ferez probablement avoir d'autres obstacles en plus des colonnes partitionnées.

La chose qui ne fonctionne pas avec les fonctions de partition est que le classement du paramètre d'entrée est stocké dans sys.partition_parameters. Ces métadonnées particulières ne sont pas modifiées avec l'option sinon génial même si non documenté - q . Les effets que cela a sont:

  1. En supposant que les données sont soit VARCHAR mais pas de changement de page de code, soit NVARCHAR, alors les données seront toujours partitionnées comme avant. Par exemple, supposons que vous ayez une fonction de partition RANGE LEFT En utilisant FOR VALUES ( 'D', 'H', 'O', 'V' ). Dans ce cas, la partition # 2 contient les valeurs > D Et <= H.

    • Lors de l'utilisation du classement insensible à la casse de SQL_Latin1_General_CP1_CI_AI, La partition # 2 contiendrait E, e, F, f, G, g, H, h. Assez simple, non?
    • Cependant, lors de l'utilisation du classement sensible à la casse de SQL_Latin1_General_CP1_CS_AI, La partition # 2 contiendrait d, E, e, F, f, G, g, H. Cette partition contient désormais les minuscules d et ne contient pas les minuscules h. Cela est dû à la plupart des classements SQL Server triant les majuscules avant les minuscules (les classements Windows trient d'abord les minuscules).
  2. Si les données sont VARCHAR et la page de codes est différente, alors les choses pourraient deviennent intéressantes. Tous les caractères dont les valeurs sont comprises entre 128 et 255 peuvent potentiellement avoir une valeur et/ou une position de tri différentes, être mappés sur un caractère similaire car il n'est pas disponible sur la page de codes du classement utilisé pour le classement des fonctions de partition (c'est-à-dire le mappage "le mieux adapté" ), ou il pourrait être manquant sur l'ancienne page de code, il devient donc un "? " et trie comme ça. En fonction des caractères utilisés, de la façon dont ils sont mappés (s'ils le font) à l'autre page de code et de la façon dont les plages de partition sont configurées, vous pouvez expérimenter tout ce qui va de "aucun effet" à "vraiment étrange et difficile" pour tracer/déboguer le comportement ".

J'ai soumis une demande pour que ces métadonnées particulières soient ajoutées à l'opération sqlservr.exe -q , mais Microsoft n'a aucune réelle incitation à le corriger étant donné que c'est une fonctionnalité non documentée. Donc, veuillez le soutenir (c'est-à-dire voter pour) afin qu'ils puissent au moins voir que les gens le veulent (merci!): Mettre à jour le classement des fonctions de partition et des types de table définis par l'utilisateur via SQLSERVR -Q

Cela dit, les deux principales questions sont les suivantes:

  1. Quel est l'effet de changer le classement en premier lieu? Quel est le classement actuel et quel est le nouveau classement?

    Si la page de codes change, y aura-t-il une perte de données (l'exemple de code dans la question montre une colonne VARCHAR, ce qui en fait une possibilité; pas pour NVARCHAR)? Cette approche ne pas effectue une conversion de page de code, donc les caractères avec des valeurs entre 128 et 255 peut-être deviennent un autre caractère, et il n'y a pas d'avertissement/d'erreur lorsque cela se produit (mais si vous ne disposez pas de telles données, ce n'est pas un problème).

    Si les sensibilités changent (c'est-à-dire en passant de sensible à l'accent à insensible à l'accent), y aura-t-il maintenant des violations d'unicité dans un index/contrainte unique existant?

  2. Voulez-vous que le nouveau classement soit utilisé pour TOUT? Si c'est le cas, cela pourrait fonctionner. Sinon, cette approche n'est pas viable car elle change TOUT.

Pour plus de détails sur cette approche, veuillez consulter mon article:

Modification du classement de l'instance, des bases de données et de toutes les colonnes dans toutes les bases de données utilisateur: qu'est-ce qui pourrait mal tourner? (assurez-vous de consulter la section "Recherche et correction des objets ignorés" qui contient des requêtes pour vous aider avec fixation des fonctions de partition)

Si vous avez plusieurs bases de données et que vous souhaitez uniquement modifier celle-ci (encore une fois, TOUT dans cette base de données, y compris le classement par défaut de la base de données), vous pouvez:

  1. Détachez (ou sauvegardez) cette base de données.
  2. Créez une nouvelle instance temporaire juste pour effectuer cette modification, en utilisant le même classement au niveau de l'instance utilisé pour l'instance à laquelle cette base de données appartient actuellement.
  3. Attachez (ou restaurez) la base de données à la nouvelle instance temporaire.
  4. Mettez à jour le classement de l'instance temporaire à l'aide de la méthode sqlservr.exe -q.
  5. Détachez (ou sauvegardez) la base de données de l'instance temporaire.
  6. Rattachez (ou restaurez) la base de données à l'instance dont elle provient.
7
Solomon Rutzky

Selon la documentation de Microsofts:

ALTER TABLE ... ALTER COLUMN...

a quelques restrictions concernant la colonne à modifier

La colonne modifiée ne peut pas être:

  • Une colonne avec un type de données d'horodatage.
  • ROWGUIDCOL de la table.
  • Une colonne calculée ou utilisée dans une colonne calculée.
  • Utilisé dans les statistiques générées par l'instruction CREATE STATISTICS. Sauf si la colonne est un type de données varchar, nvarchar ou varbinary, le type de données n'est pas modifié. Et, la nouvelle taille est égale ou supérieure à l'ancienne taille. Ou, si la colonne passe de non nul à nul. Tout d'abord, supprimez les statistiques à l'aide de l'instruction DROP STATISTICS.
  • Les statistiques générées automatiquement par l'optimiseur de requêtes sont automatiquement supprimées par ALTER COLUMN.
  • Utilisé dans une contrainte PRIMARY KEY ou [FOREIGN KEY] REFERENCES.
  • Utilisé dans une contrainte CHECK ou UNIQUE. Mais, la modification de la longueur d'une colonne de longueur variable utilisée dans une contrainte CHECK ou UNIQUE est autorisée.
  • Associé à une définition par défaut. Cependant, la longueur, la précision ou l'échelle d'une colonne peuvent être modifiées si le type de données n'est pas modifié.

Si cela est possible du fait de la quantité de tables - j'exporterais la base de données dans un script TSQL, apporter les modifications nécessaires concernant les classements - à condition que cela n'affecte pas les données de chaîne dans les tables (varchar/nvarchar/text et similaire types), puis reconstruisez le serveur à l'aide dudit script TSQL.

1
eagle275