SQL Standard Edition ALTER TABLE ADD column NOT NULL CONSTRAINT DEFAULT 0 (Blocking and taking a long time)
The application is down! Blocking alerts are flooding your email inbox! SQL Server is working as fast as it can but it's still your problem. Kill SPID, kill kill kill kill waiting for Roll Back. We need to find a new way to do the same thing....
Now what just happened?
The creation of a new NOT NULL column with a default value to an existing table operation occurred on a large table: where every row in the table is trying to updated to add the default value of the new column.
Now why did this happen?
You are running SQL Standard 2016 or an Enterprise version prior to SQL 2012. Small tables this is fairly insignificant as it will be a fast operation, but for large tables this will be problematic as to completely prohibit the operation as it will be a long operation causing blocking which will lead to application and job timeouts.
How can we reduce this and can we?
Starting with SQL Server 2012 and if you are on ENTERPRISE Edition the operation in most cases, will be online and instantaneous as only the table metadata is changed with no rows being update assuming you are not trying to do this on a varchar(max) nvarchar(max) varbinary(max) or XML data type. In our test environment we were using 4 million rows on a 3GB database with a SQL server that had 12GB ram and were not able to product locking/blocking if data was loaded into cache. On production 80GB databases 40MM rows and 28GB of ram and step #3 produced around 5 minutes of down time as we paged out to disk and another 5 minutes on the FK additions.
If you are on SQL Standard or an older SQL version you can use a 3 step process to minimize locking/blocking and application downtime. However, you are still only able to reduce the total duration and will still have some downtime.
-- Initial script that was terminated due to running 6+ minutes and locking/blocking on the table
-- When tested on a full backup it ran for 36 minutes
ALTER TABLE [dbo].[SomeTable] ADD [SomeColumn] INT NOT NULL
CONSTRAINT [DF_SomeTable_SomeColumn] DEFAULT (0)
GO
ALTER TABLE [dbo].[SomeTable] ADD CONSTRAINT [FK_SomeTable_SomeColumn] FOREIGN KEY ([SomeColumn]) REFERENCES [dbo].[SomeOtherTable] ([SomeOtherColumn])
GO
--step #1
-- When tested on a full backup it ran for 36 minutes
ALTER TABLE [dbo].[SomeTable] ADD [SomeColumn] INT NOT NULL
CONSTRAINT [DF_SomeTable_SomeColumn] DEFAULT (0)
GO
ALTER TABLE [dbo].[SomeTable] ADD CONSTRAINT [FK_SomeTable_SomeColumn] FOREIGN KEY ([SomeColumn]) REFERENCES [dbo].[SomeOtherTable] ([SomeOtherColumn])
GO
--step #1
--add SomeTable.SomeColumn as Null with Default Value
ALTER TABLE [dbo].[SomeTable] ADD [SomeColumn] INT NULL
CONSTRAINT [DF_SomeTable_SomeColumn] DEFAULT (0) -- Naming convention format DF_TableName_ColumnName for default constraint
GO
--step #2 --
--clean up NULLs in table. This will and can still run for long durations of time....
SET NOCOUNT ON;
DECLARE @BatchSize INT = 10000 -- Pick your batch size
WHILE 1=1
BEGIN
UPDATE TOP (@BatchSize) [dbo].[SomeTable]
SET [SomeColumn] = 0
WHERE [SomeColumn] IS NULL
IF @@ROWCOUNT < @BatchSize
BREAK;
END
GO
--step #3 -- This blocking
--modify status column to NOT NULL...
ALTER TABLE [dbo].[SomeTable] ALTER COLUMN [SomeColumn] INT NOT NULL
GO
-- You are now done and free to add any FOREIGN KEY CONSTRAINT as needed. **However, Note that this operation will cause blocking as well and could take as long or longer than step #3 above.
Comments
Post a Comment