开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜