Ich habe eine große (~ 67 Millionen Zeilen) Name-Wert-Tabelle mit Volltextindizierung für die Spalte DataValue
.
Wenn ich versuche, den folgenden Befehl auszuführen:
ALTER TABLE VisitorData ADD NumericValue bit DEFAULT 0 NOT NULL;
Es läuft 1 Stunde 10 Minuten und wird in einer VisitorData
-Tabelle, die ~ 67 Millionen Zeilen enthält, immer noch nicht abgeschlossen.
Hier sind weitere Einzelheiten zur Tabelle:
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
Die Wartetypen, die während des ALTER TABLE
Befehl sind LCK_M_SCH_M
(Schemaänderung) gemäß den folgenden Abfrageergebnissen:
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
Ich arbeite mit Produktionsservern, auf denen SQL Server 2005 ausgeführt wird SP 2 (wird bald auf 2008 SP2 aktualisiert).
Die Schemaänderung dauert so lange, weil Sie der Spalte während der Änderung einen Standardwert zuweisen und diesen mit einer nicht nullbaren Spalte erzwingen. Außerdem muss die Spalte mit mehr als 60 Millionen Zeilen gefüllt werden. Dies ist eine unglaublich teure Operation. Ich bin nicht sicher, was Ihre Anwendungsanforderungen sind, aber ein Ansatz, der die Schemaänderung beschleunigen würde, besteht darin, es als nullfähige Spalte ohne Standardwert hinzuzufügen und dann eine Aktualisierung in Stapeln durchzuführen, um 0 als Wert für die Spalte zuzuweisen. Nachdem Ihre Aktualisierung abgeschlossen ist, können Sie eine weitere Schemaänderung anwenden, um die Spalte in nicht nullbar zu ändern und den Standardwert zuzuweisen.
Die Volltextindizierung ist für Ihr Problem wahrscheinlich irrelevant. Vor SQL Server 2012 war das ADD COLUMN NOT NULL DEFAULT ...
ist eine Offline-Operation, bei der ein Update ausgeführt und jede Zeile mit dem neuen Standardwert der neu hinzugefügten Spalte gefüllt werden muss. In SQL Server 2012+ ist der Vorgang viel schneller (siehe Online nicht NULL mit Wertespalte in SQL Server 11 hinzufügen , da nur die Metadaten der Tabelle aktualisiert werden und tatsächlich keine Zeilen aktualisiert werden.
Ihre ALTER TABLE
ist aufgrund des Updates höchstwahrscheinlich langsam. Denken Sie daran, da dies eine einzelne Transaktion ist, wird ein riesiges Protokoll generiert, und Ihr Protokoll wächst wahrscheinlich jetzt und wird bei seiner Erweiterung ständig auf Null gesetzt. Aufgrund gewöhnlicher Konflikte kann es jedoch auch langsam sein: Die Anweisung kann möglicherweise die SCH-M-Sperre für den Tisch nicht abrufen. Anschauen sys.dm_exec_requests
sollte zeigen, ob dies der Fall ist, das wait_type
und wait_resource
Spalten würden anzeigen, ob die Anweisung ALTER
blockiert ist oder Fortschritte macht.
Antwort, die ursprünglich vom Autor zu der Frage hinzugefügt wurde:
Gemäß Jasons Antwort habe ich stattdessen das folgende Update veröffentlicht:
ALTER TABLE VisitorData ADD NumericValue bit NULL
Dies wurde schließlich ausgeführt, dauerte jedoch 29 Minuten und 16 Sekunden. Die Operation selbst sollte ziemlich schnell sein (nur Metadaten), daher stelle ich mir vor, dass fast die gesamte Zeit darauf gewartet wurde, das notwendige LCK_M_SCH_M
(Schemamodifikation) sperren.
Mit dem neuen Feld bit
konnte ich schnell den Standardwert über das Skript hinzufügen:
ALTER TABLE VisitorData ADD
CONSTRAINT DF_VisitorData_NumericValue DEFAULT(0) FOR NumericValue;
Ich bin gerade dabei, alle NumericValue
Bits in der Tabelle mithilfe einer benutzerdefinierten Funktion zu setzen (siehe unten). Es wird ausgeführt und dauert ungefähr 1 Minute pro 1 Million Zeilen in der ~ 68 Millionen-Zeilentabelle.
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
Sobald dies abgeschlossen ist, plane ich, die endgültige Schemaanpassung auszuführen, damit diese neue Bitspalte nicht null ist:
ALTER TABLE VisitorData ALTER COLUMN NumericValue bit NOT NULL;
Hoffentlich wird dieses letzte Schema-Update schnell ausgeführt, sobald alle Werte nicht null sind und der Standardwert NumericValue
vorhanden ist.