SQL Server performance for alter table alter column change data type
We need to change the data types of some columns from int to bigint. Unfortunately some of these tables are large, around 7-10 millio开发者_运维知识库n rows (but not wide).
Alter table alter column is taking forever on these tables. Is there a faster way to achieve this?
Coincidentally, I had to do something very similar about 3 hours ago. The table was 35m rows, it is fairly wide, and it was taking forever to just do this:
alter table myTable add myNewColumn int not null default 0;
Here's what what I ended up going with:
alter table myTable add myNewColumn int null;
while 1=1
begin
update top (100000) myTable
set
myNewColumn = 0
where
myNewColumn is null;
if @@ROWCOUNT = 0 break;
end
alter table myTable alter column myNewColumn int not null;
alter table myTable add constraint tw_def_myNewColumn default (0) for myNewColumn;
This time around, the alter table
statements were near-instant. It took about 7-8 minutes (on a slow server) to do the update batches. I'm speculating that SQL Server was generating undo in my original query to restore the values, but I didn't expect that starting off.
Anyway, in your case, maybe something similar would help. You could try adding a new bigint column, update the new column in batches, then set the constraints on it.
create the new table you desire, with the right columntypes and indices. (script out the old table, and change the name.)
insert into new table (column list) select * from old_table;
rename the old_table old_table_back, rename new_table old_table.
create the old indices on the new table, drop any ri constraints on the old table and creaet them on the new table. Again, your rdbms will have some easy way to generate scripts to do this.
I just ran across this issue... A table with 447,732,310 records in it. Had a coworker come up with an awesome solution, only took about 24 minutes to copy data to a new table, about 40 minutes to create indexes.
Here's what we did:
--Get ntiles of idOrders, split up into 100 groups - 1:20 minutes
IF(OBJECT_ID('TEMPDB..#x')) IS NOT NULL
DROP TABLE #x
SELECT nt, MIN(idOrder) idOrderMin, MAX(idOrder) idOrderMax
INTO #X
FROM (
SELECT idOrder, NTILE(100) OVER(ORDER BY idOrder) nt
FROM (
SELECT DISTINCT idOrder FROM order_raw_fields
) X
) Y
GROUP BY nt
-- view results
--SELECT * FROM #x ORDER BY idOrderMin
-- create new table
SELECT TOP 0 *
INTO ORDER_RAW_FIELDS_Intl
FROM ORDER_RAW_FIELDS
ALTER TABLE dbo.ORDER_RAW_FIELDS_Intl
ALTER COLUMN value nvarchar(500)
--Build queries
SELECT 'insert into ORDER_RAW_FIELDS_Intl select * from order_raw_fields
where idOrder >= ' + CAST(idOrderMIn AS VARCHAR(100)) + ' and idOrder <= ' + CAST(idOrderMax AS varchar) InsertStmt
INTO #inserts
FROM #X
ORDER BY idOrderMin
DECLARE insertCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT InsertStmt
FROM #inserts
OPEN insertCursor
-- 24:04 minute execution time to match
DECLARE @insertStmt NVARCHAR(125)
FETCH NEXT FROM insertCursor INTO @insertStmt
WHILE @@FETCH_STATUS = 0
BEGIN
--EXECUTE @insertStmt
EXECUTE sp_executesql @statement=@insertStmt
PRINT 'Execution Complete: ' + @insertStmt
FETCH NEXT FROM insertCursor INTO @insertStmt
END
CLOSE insertCursor
DEALLOCATE insertCursor
-- Add indexes
-- 21:37 minutes completion time
ALTER TABLE [dbo].[ORDER_RAW_FIELDS_Intl] ADD CONSTRAINT [PK_ORDER_RAW_FIELDS_Intl] PRIMARY KEY CLUSTERED
(
[idRow] ASC,
[idOrder] ASC,
[remoteFieldName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 92) ON [PRIMARY]
GO
-- 13:45 minutes completion time
CREATE NONCLUSTERED INDEX [IX_idOrder_remoteFieldName2] ON [dbo].[ORDER_RAW_FIELDS_Intl]
(
[idOrder] ASC,
[remoteFieldName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 94) ON [PRIMARY]
GO
-- drop table
TRUNCATE TABLE [dbo].[ORDER_RAW_FIELDS]
DROP TABLE [dbo].[ORDER_RAW_FIELDS]
-- renamed new table to old tables's name
EXEC sp_rename 'ORDER_RAW_FIELDS_Intl', 'ORDER_RAW_FIELDS';
I just ran into this a few weeks ago with a table with 639m rows. I ended up creating a new table and copying the data over in "batches". It took about 2 days on the main server and replication took 3 days to replicate it all. I then modified all the views and procs that used to old table. This allowed me to clean up a few issues, like getting rid of a column I didn't want and picking (in some cases) better indexes. After all the data was moved sql changed, I then dropped the old table.
A mess, but am wiser for it now. Use big ints for you identity primary keys if the system will be long lived and there is any chance of multi-millions of rows.
精彩评论