Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 514
SharePoint Newbie Avatar asked Nov 30 '10 07:11

SharePoint Newbie


People also ask

Can ALTER TABLE change data type?

We can use ALTER TABLE ALTER COLUMN statement to change the datatype of the column. The syntax to change the datatype of the column is the following. In the syntax, Tbl_name: Specify the table name that contains the column that you want to change.

How do I change the field type without dropping the table?

So to do that go to SQL Server and within Tools select Options. Now in the option window expand Designers and under that "Table and Database Designers" and uncheck the check box "Prevent saving changes that require table re-creation" then click OK.

Can we change the datatype of a column in SQL?

You can modify the data type of a column in SQL Server by using SQL Server Management Studio or Transact-SQL. Modifying the data type of a column that already contains data can result in the permanent loss of data when the existing data is converted to the new type.

Does altering a table remove data?

No it will not erase your data. It will add a column and all the existing data will be assigned null values in this column.


3 Answers

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.

like image 126
Matt Avatar answered Oct 24 '22 00:10

Matt


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.

like image 25
tpdi Avatar answered Oct 24 '22 01:10

tpdi


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';
like image 37
Gary Avatar answered Oct 24 '22 02:10

Gary