J'ai une grande table de nom-valeur (~ 67 millions de lignes) qui a une indexation en texte intégral sur la colonne DataValue
.
Si j'essaie d'exécuter la commande suivante:
ALTER TABLE VisitorData ADD NumericValue bit DEFAULT 0 NOT NULL;
Il s'exécute pendant 1 heure 10 minutes et ne se termine toujours pas sur une table VisitorData
qui contient ~ 67 millions de lignes.
Voici plus de détails sur la table:
CREATE TABLE [dbo].[VisitorData](
[VisitorID] [int] NOT NULL,
[DataName] [varchar](80) NOT NULL,
[DataValue] [nvarchar](3800) NOT NULL,
[EncryptedDataValue] [varbinary](max) NULL,
[VisitorDataID] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_VisitorData_VisitorDataID] PRIMARY KEY CLUSTERED (
[VisitorDataID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [UNQ_VisitorData_VisitorId_DataName] UNIQUE NONCLUSTERED (
[VisitorID] ASC,
[DataName] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[VisitorData]
ADD CONSTRAINT [UNQ_VisitorData_VisitorDataID] UNIQUE NONCLUSTERED (
[VisitorDataID] ASC
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE [dbo].[VisitorData]
WITH CHECK ADD
CONSTRAINT [FK_VisitorData_Visitors] FOREIGN KEY([VisitorID])
REFERENCES [dbo].[Visitors] ([VisitorID])
GO
ALTER TABLE [dbo].[VisitorData]
CHECK CONSTRAINT [FK_VisitorData_Visitors] GO
CREATE FULLTEXT CATALOG DBName_VisitorData_Catalog WITH ACCENT_SENSITIVITY = ON
CREATE FULLTEXT INDEX ON VisitorData ( DataValue Language 1033 )
KEY INDEX UNQ_VisitorData_VisitorDataID
ON DBName_VisitorData_Catalog
WITH CHANGE_TRACKING AUTO
GO
Les types d'attente qui se produisent pendant le ALTER TABLE
commande sont LCK_M_SCH_M
(modification de schéma), selon les résultats de la requête ci-dessous:
select * from sys.dm_os_waiting_tasks
waiting_task_address session_id exec_context_id wait_duration_ms wait_type resource_address blocking_task_address blocking_session_id blocking_exec_context_id resource_description
-------------------- ---------- --------------- -------------------- -------------------- ------------------ --------------------- ------------------- ------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0x0000000000B885C8 54 0 112695 LCK_M_SCH_M 0x00000000802DF600 0x000000000054E478 25 0 objectlock lockPartition=0 objid=834102012 subresource=FULL dbid=5 id=lock438a02e80 mode=IS associatedObjectId=834102012
0x0000000000B885C8 54 0 112695 LCK_M_SCH_M 0x00000000802DF600 0x00000000088AB048 23 0 objectlock lockPartition=0 objid=834102012 subresource=FULL dbid=5 id=lock438a02e80 mode=IS associatedObjectId=834102012
Je travaille avec des serveurs de production qui exécutent SQL Server 2005 SP 2 (qui sera bientôt mis à niveau vers 2008 SP2).
Le schéma le modifie si longtemps car vous attribuez une valeur par défaut à la colonne pendant la modification et l'imposez avec une colonne non nullable, et il doit remplir la colonne pour plus de 60 millions de lignes, ce qui est une opération incroyablement coûteuse. Je ne sais pas quelles sont les exigences de votre application, mais une approche qui rendrait le schéma plus rapide consiste à l'ajouter en tant que colonne nullable sans valeur par défaut, puis à effectuer une mise à jour par lots pour attribuer 0 comme valeur à la colonne. Une fois votre mise à jour terminée, vous pouvez appliquer une autre modification de schéma pour modifier la colonne en non nullable et attribuer la valeur par défaut.
L'indexation de texte intégral n'est probablement pas pertinente pour votre problème. Avant SQL Server 2012, le ADD COLUMN NOT NULL DEFAULT ...
est une opération hors ligne qui doit exécuter une mise à jour et remplir chaque ligne avec la nouvelle valeur par défaut de la colonne nouvellement ajoutée. Dans SQL Server 2012+, l'opération est beaucoup plus rapide, voir en ligne non NULL avec une colonne de valeurs ajoutée dans SQL Server 11 car il ne met à jour que les métadonnées de la table et ne met pas à jour les lignes.
Votre ALTER TABLE
est très probablement lent à cause de la mise à jour. N'oubliez pas, puisqu'il s'agit d'une seule transaction, un énorme journal sera généré et votre journal est probablement en train de croître maintenant et d'être constamment mis à zéro à mesure qu'il se développe. Cependant, il peut également être lent en raison d'une contention ordinaire: l'instruction peut ne pas être en mesure d'acquérir le verrou SCH-M sur la table. Regarder sys.dm_exec_requests
devrait indiquer si tel est le cas, le wait_type
et wait_resource
les colonnes indiqueraient si l'instruction ALTER
est bloquée ou progresse.
Réponse initialement ajoutée à la question par son auteur:
Selon réponse de Jason , j'ai plutôt publié la mise à jour suivante:
ALTER TABLE VisitorData ADD NumericValue bit NULL
Cela a finalement été exécuté, mais a pris 29 minutes et 16 secondes. L'opération elle-même devrait être assez rapide (métadonnées uniquement), donc j'imagine que presque tout ce temps a été passé à attendre pour acquérir le LCK_M_SCH_M
(modification du schéma) verrou.
Avec le nouveau champ bit
en place, j'ai pu y ajouter rapidement la valeur par défaut via le script:
ALTER TABLE VisitorData ADD
CONSTRAINT DF_VisitorData_NumericValue DEFAULT(0) FOR NumericValue;
Je suis en train de définir tous les bits NumericValue
dans le tableau à l'aide d'une fonction définie par l'utilisateur (voir ci-dessous). Il est en cours et prend environ 1 minute par 1 million de lignes dans le tableau ~ 68 millions de lignes.
WITH RD_CTE (VisitorD, DataName)
AS
(
SELECT TOP 10000 VisitorD, DataName
FROM VisitorData WITH (NOLOCK)
WHERE NumericValue IS NULL
)
UPDATE VisitorData
SET NumericValue = CASE WHEN dbo.ufn_IsReallyNumeric(rd.DataValue) = 1 THEN 1 ELSE 0 END
FROM VisitorData rd WITH (NOLOCK)
INNER JOIN RD_CTE rdc WITH (NOLOCK) ON rd.VisitorD = rdc.VisitorD AND rd.DataName = rdc.DataName
GO 6800
Une fois cela terminé, je prévois d'exécuter l'ajustement de schéma final pour rendre cette nouvelle colonne de bits non nulle:
ALTER TABLE VisitorData ALTER COLUMN NumericValue bit NOT NULL;
Espérons que cette dernière mise à jour du schéma s'exécutera rapidement une fois que toutes les valeurs seront non nulles et que la valeur par défaut NumericValue
sera en place.