Sqlserver數據遷移踩坑記


一. 背景

老的庫source_db是安裝在window server 2003上, 新庫target_db為華為雲的SqlServer 2008(RDS),  老庫數據遷移到新庫上。

二. 遷移步驟

1. 為了保證在遷移過程中源庫數據不再被更新,先將庫設置為只讀。

方法1:可以使用如下命令來設置sql server數據庫的只讀特性。

USE [zssg]
GO
ALTER DATABASE [TESTDB] SET READ_ONLY WITH NO_WAIT
GO

方法2:也可以可視化界面設置(Sqlserver managament studio 2008), 右鍵數據庫 -> 屬性,進入如下頁面

 

 

2. 源庫導出數據到目標庫

右鍵數據庫 -> 任務 -> 導出數據, 如下圖:

 

 

 

 直接下一步

 

 

源庫的服務器名稱會自動帶出來, 也可以自己手動編輯(IP,端口)的格式, 比如:  192.168.1.20,1433。

輸入賬戶名密碼后,點擊“刷新”, 選擇數據庫, 然后點擊“下一步”。

 

 

 

填寫目標庫的信息, 然后下一步。

 

 

 

 

全選表和視圖, 然后點擊“編輯映射”

 

 

 

立即運行,如下:

 

 

 

點擊“完成”, 開始遷移

 

 

 

遷移成功,如下:

 

 

 

 進入我們目標庫,發現遷移過來的表結構, 丟失了所有的表主鍵、索引、自增序列。 故我們需要進行再處理。

三、遷移后表結構處理

處理邏輯:

1. 目標庫所在服務器再新建一個庫(命名為mid_db), 把源庫的表結構(不包含數據)拷貝過來(可以通過navicat的數據傳輸,如下圖)

 

 

 2. 點擊tab中的“高級”,如下設置:(表選項只需要勾選“包含自動遞增”, 索引等那些先不要加,否則做數據插入會很慢!), 然后點擊右下角的“開始”, 進行表結構遷移到mid_db庫

 

 

 

3. 拷貝表結構完畢, 如下圖:

 

 

 

4. 對mid_db庫的所有表進行重命名(如下為構建重命名的sql, 表名增加統一的前綴newfix_)

select 'exec sp_rename ''' + name + ''',''newfix_' + name + ''';' from sys.tables;

執行查詢結果的sql, 重命名完畢。

5. 把mid_db的所有表結構拷貝到target_db中。(方法為同上 3.1 - 3.4 步驟)

 

6. 構建insert語句, 把target表的不帶newfix_前綴的表數據拷貝到帶前綴newfix_的表

-- 有自增字段的
select name,'set identity_insert newfix_' +name+' ON; insert into newfix_'+name+ '('+columns+ ') select '+columns+ ' from '+ name+ ' where 1=1;'+'set identity_insert newfix_' +name+' OFF;' from (
SELECT a.name, 
         columns = stuff((
                 SELECT ',[' + b.name + ']'
                   FROM sys.columns B join sys.tables c on B.object_id = c.object_id where  c.name = a.name
                    FOR xml path('')) , 1 , 1 , '')
    FROM sys.tables A where 1=1 and a.name not like 'newfix_%' group by a.name
)aa where exists(
select 1 from syscolumns where id=object_id('newfix_' + aa.name) and status=0x80
) order by aa.name;

select *from sys.tables A where 1=1 and a.name not like 'newfix_%'

-- 沒有自增字段的
select name,'insert into newfix_'+name+ '('+columns+ ') select '+columns+ ' from '+ name+ ' where 1=1;' from (
SELECT a.name, 
         columns = stuff((
                 SELECT ',[' + b.name + ']'
                   FROM sys.columns B join sys.tables c on B.object_id = c.object_id where  c.name = a.name
                    FOR xml path('')) , 1 , 1 , '')
    FROM sys.tables A where 1=1 and a.name not like 'newfix_%' group by a.name
)aa where not exists(
select 1 from syscolumns where id=object_id('newfix_' + aa.name) and status=0x80
) order by aa.name;

 

執行上面查詢結果的SQL, 成功把數據插入到有自增機制的帶newfix_前綴的新表中。

 

7. 把target_db的所有不帶newfix_的表重命名為 oldfix_, 把所有帶newfix_表重命名為去掉newfix_的表

select 'exec sp_rename ''' + name + ''',''oldfix_' + name + ''';' from sys.tables where name not like 'newfix_%';

select 'exec sp_rename ''' + name + ''',''' + REPLACE (name, 'newfix_','') + ''';' from sys.tables where name like 'newfix_%';

執行如上的查詢結果, 實現了表的替換。

 

8. 查詢source_db的所有索引,用於構建target_db庫的所有表的索引。

BEGIN
    WITH tx AS (
        SELECT
            a.object_id,
            b.name AS schema_name,
            a.name AS table_name,
            c.name AS ix_name,
            c.is_unique AS ix_unique,
            c.type_desc AS ix_type_desc,
            d.index_column_id,
            d.is_included_column,
            e.name AS column_name,
            f.name AS fg_name,
            d.is_descending_key AS is_descending_key,
            c.is_primary_key,
            c.is_unique_constraint
        FROM
            sys.tables AS a
        INNER JOIN sys.schemas AS b ON a.schema_id = b.schema_id
        AND a.is_ms_shipped = 0
        INNER JOIN sys.indexes AS c ON a.object_id = c.object_id
        INNER JOIN sys.index_columns AS d ON d.object_id = c.object_id
        AND d.index_id = c.index_id
        INNER JOIN sys.columns AS e ON e.object_id = d.object_id
        AND e.column_id = d.column_id
        INNER JOIN sys.data_spaces AS f ON f.data_space_id = c.data_space_id
    ) SELECT
        Drop_Index = CASE
    WHEN (
        a.is_primary_key = 1
        OR a.is_unique_constraint = 1
    ) THEN
        'ALTER TABLE ' + a.table_name + ' DROP CONSTRAINT ' + a.ix_name + ';'
    ELSE
        'DROP INDEX ' + a.ix_name COLLATE SQL_Latin1_General_CP1_CI_AS + ' ON ' + a.schema_name + '.' + a.table_name + ';'
    END,
    Create_Index = CASE
WHEN (
    a.is_primary_key = 1
    OR a.is_unique_constraint = 1
) THEN
    'ALTER TABLE ' + a.table_name + ' ADD CONSTRAINT ' + a.ix_name + CASE
WHEN a.is_primary_key = 1 THEN
    ' PRIMARY KEY'
ELSE
    ' UNIQUE'
END + '(' + indexColumns.ix_index_column_name + ');'
ELSE
    'CREATE ' + CASE
WHEN a.ix_unique = 1 THEN
    'UNIQUE '
ELSE
    ''
END + a.ix_type_desc + ' INDEX ' + a.ix_name COLLATE SQL_Latin1_General_CP1_CI_AS + ' ON ' + a.schema_name + '.' + a.table_name + '(' + indexColumns.ix_index_column_name + ')' + CASE
WHEN IncludeIndex.ix_included_column_name IS NOT NULL THEN
    ' INCLUDE (' + IncludeIndex.ix_included_column_name + ')'
ELSE
    ''
END + ' ON [' + a.fg_name + '];'
END,
 CASE
WHEN a.ix_unique = 1 THEN
    'UNIQUE'
END AS ix_unique,
 a.ix_type_desc,
 a.ix_name,
 a.schema_name,
 a.table_name,
 indexColumns.ix_index_column_name,
 IncludeIndex.ix_included_column_name,
 a.fg_name,
 a.is_primary_key,
 a.is_unique_constraint
FROM
    (
        SELECT DISTINCT
            ix_unique,
            ix_type_desc,
            ix_name,
            schema_name,
            table_name,
            fg_name,
            is_primary_key,
            is_unique_constraint
        FROM
            tx
    ) AS a OUTER APPLY (
        SELECT
            ix_index_column_name = STUFF(
                (
                    SELECT
                        ',' + column_name + CASE
                    WHEN is_descending_key = 1 THEN
                        ' DESC'
                    ELSE
                        ''
                    END
                    FROM
                        tx AS b
                    WHERE
                        schema_name = a.schema_name
                    AND table_name = a.table_name
                    AND ix_name = a.ix_name
                    AND ix_type_desc = a.ix_type_desc
                    AND fg_name = a.fg_name
                    AND is_included_column = 0
                    ORDER BY
                        index_column_id FOR XML PATH ('')
                ),
                1,
                1,
                ''
            )
    ) IndexColumns OUTER APPLY (
        SELECT
            ix_included_column_name = STUFF(
                (
                    SELECT
                        ',' + column_name
                    FROM
                        tx AS b
                    WHERE
                        schema_name = a.schema_name
                    AND table_name = a.table_name
                    AND ix_name = a.ix_name
                    AND ix_type_desc = a.ix_type_desc
                    AND fg_name = a.fg_name
                    AND is_included_column = 1
                    ORDER BY
                        index_column_id FOR XML PATH ('')
                ),
                1,
                1,
                ''
            )
    ) IncludeIndex where 1=1
ORDER BY
    a.schema_name,
    a.table_name,
    a.ix_name
END

取Create_Index列數據執行

 

 

執行完畢后,則所有索引創建完畢。

9. 刪除無用的中間表 oldfix_前綴的。

select 'drop table ' + name + ';' from sys.tables where name like 'oldfix_%';

10. 刪除無用的中間庫 mid_db

drop database mid_db;

 

其他的函數、視圖和觸發器可進行單獨遷移(使用如上的SSMS或者navicat都可以), 不再贅述。

 

四、總結

1. 采用中間庫mid_db的原因是源庫source_db無法一步到位遷移成功到target_db, 因為SSMS工具做數據遷移后, 新庫的表索引和自增序列等信息會丟失, mid_db和target_db的區別是多了自增屬性。(target_db表不能直接修改字段為自增, 必須先drop column后 add column ,會導致數據丟失,故我們采用表數據拷貝的方式,防止數據丟失)

2. target_db的表在插入數據之前, 保證表是沒有索引的, 否則會奇慢無比(頻繁插入導致表索引字段記錄不斷重排), 插入完畢后再創建索引。

3. SSMS工具對同一個機子的不同數據庫間做數據遷移速度也很慢, 故不使用SSMS進行 target_db到mid_db的數據遷移, 而是把mid_db表拷貝到target_db, 然后在target_db中進行insert的表數據拷貝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM