简体   繁体   中英

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 million 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM