SQL SERVER 自動生成 MySQL 表結構及索引 的建表SQL


 

 
    SQL SERVER的表結構及索引轉換為MySQL的表結構及索引,其實在很多第三方工具中有提供,比如navicat、sqlyog等,但是,在處理某些數據類型、默認值及索引轉換的時候,總有些不盡人意並且需要安裝軟件,懶人開始想法子,所以基於SQL SERVER,寫了一個存儲過程,可以根據表名直接轉換為MySQL的建表建索引的SQL腳本(針對 MySQL Innodb引擎)。目前不支持分區表的分區配置及區域數據類型的轉換。
 


 
    如果轉載,請注明博文來源:  www.cnblogs.com/xinysu/   ,版權歸 博客園 蘇家小蘿卜 所有。望各位支持!
 


 

    建表的SQL中,主要在數據類型轉換、主鍵及索引的處理。

1 數據類型轉換

    數據類型轉換表詳見下表,這些數據類型的轉換目前已測試過,均可正常使用。
    但是注意兩類數據庫存儲數據的一些差異,看下能否接受:
  • SQL SERVER中的datetime,保留到微秒(秒后小數點3位),而mysql僅保留到秒,轉換后是否會影響業務,如果影響,需要新增一個字段專門來存儲微秒或者毫秒,雖然mysql中沒有時間數據類型的精度到達微秒或者毫秒,但是mysql提供對微秒的相關處理函數microsecond、extract跟date_format。
  • MySQL使用tinyint代替SQL SERVER的bit
  • SQL SERVER的money類型使用decimal替代
  • timestamp的轉換,SQL SERVER中是一個16進制的數字,代表時間戳,每次修改都會數值都會變大。
    • 從功能考慮,轉換為mysql的時候,處理為timestamp的數據類型,默認值為CURRENT_TIMESTAMP,行發生修改則定時修改這一列數據,如果這樣轉換,那么在SQL SERVER導入數據的時候,就要相應處理。(本文存儲過程默認這么處理)
    • 從數據考慮,轉換為mysql的時候,處理為bigint的數據類型(修改存儲過程case when b.name = 'timestamp' then ' timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ' 為case when b.name = 'timestamp' then ' bigint ' )
  • 自增處理,mysql的自增步長跟增量值是整個實例統一的,不能每個表格動態修改,所以這里在轉化的過程中,為auto_increment,根據實例的設置來處理
 
ID SQL SERVER MySQL Description
1 bigint bigint  
2 binary binary  
3 bit tinyint SQL SERVER的bit類型,對於零,識別為False,非零值識別為True。
MySQL中沒有指定的bool類型,一般都使用tinyint來代替
4 char char  
5 date date  
6 datetime datetime 注意,mssql的保留到微秒(秒后小數點3位),而mysql僅保留到秒
7 datetime2 datetime 注意,mssql的保留到微秒(秒后小數點7位),而mysql僅保留到秒
8 datetimeoffset datetime 注意,mssql的保留時區,這個需要程序自己轉換
mssql的保留到微秒(秒后小數點7位),而mysql僅保留到秒
9 decimal decimal  
10 float float  
11 int int  
12 money float 默認轉換為decimal(19,4)
13 nchar char SQL SERVER轉MySQL按正常字節數轉就可以
14 ntext text  
15 numeric decimal  
16 nvarchar varchar  
17 real float  
18 smalldatetime datetime  
19 smallint smallint  
20 smallmoney float 默認轉換為decimal(10,4)
21 text text  
22 time time 注意,mssql的保留到秒后小數點8位,而mysql僅保留到秒
23 timestamp timestamp 注意,mssql的行時間戳,處理為mysql的 timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 。這會對后面導數據造成影響,從功能方面來看,可以按照上文轉換;如果從數據來看,若需要轉換16進制的文字存儲到mysql中,則這里設置為bigint即可,在表格中的臨時表中設置。
24 tinyint tinyint  
25 uniqueidentifier varchar(40) 對應mysql的UUID(),設置為文本類型即可。
26 varbinary varbinary  
27 varchar varchar  
28 xml text mysql不支持xml,修改為text
 

2 主鍵處理

    MySQL不支持非主鍵的聚集索引,也就是聚集索引則是主鍵。故在轉換的過程中,主鍵是根據SQL SERVER表格中的聚集索引來轉換的。
 
--SQL SERVER根據聚集索引的列情況來創建mysql的主鍵
SELECT
       col_name(i.object_id,ik.column_id) pk_col
FROM sys.indexes i
JOIN sys.index_columns ik ON i.index_id=ik.index_id and i.object_id=ik.object_id
WHERE i.type=1
      and i.object_id=object_id('tb')
ORDER BY key_ordinal

3 索引添加

    由於聚集索引已處理添加為主鍵,在建表的SQL中已判斷,這里則只處理非聚集索引。
    處理過程中注意:
  • MySQL不支持INCLUDE選項的包含索引,所以在處理的過程中,INCLUDE列添加到索引列中
  • MySQL 不支持WHERE選項的過濾索引,所以在處理的過程中,WHERE選項去除
  • 索引名字處理:包含列1-2個的,直接IX_表名,超過3個列的,取每列前3個字符,整個索引名長度不超過64個字符,超過截取前64個字符

4 測試

    存儲過程 [p_tb_mssqltomysql] 僅含一個參數 @tbsql,用於存儲表格名字,多個表格名中間有逗號隔開,不要有空格或者其他符號。
   

    這里,嘗試創建一個新表來測試。  

    創建表格及對應索引信息測試如下:
 1 CREATE TABLE tbtest(
 2 id INT IDENTITY(1,1) NOT NULL ,
 3 name NVARCHAR(50) NOT NULL,
 4 phone VARCHAR(11) NOT NULL,
 5 age int default 99 ,
 6 birthday datetime default getdate(),
 7 addresss text,
 8 monyes money default 123456789012345.1234,
 9 smonyes smallmoney,
10 nums int default 2,
11 moneys money,
12 smo smallmoney,
13 curversion timestamp
14 )
15  
16 ALTER TABLE tbtest ADD CONSTRAINT PK_tbtest PRIMARY KEY (ID,phone);
17 CREATE INDEX IX_NAME ON tbtest(NAME);
18 CREATE INDEX IX_phone_age ON tbtest(phone,age);
19 CREATE INDEX IX_nums ON tbtest(nums) WHERE nums>2;
20 CREATE INDEX IX_birthday ON tbtest(birthday) include (name,phone);
 
    執行存儲過程轉化:
 
 1 exec p_tb_mssqltomysql 'tbtest'
 2  
 3  
 4 CREATE TABLE tbtest(id int  not null  auto_increment 
 5 ,name varchar(50) not null 
 6 ,phone varchar(11) not null 
 7 ,age int  null 
 8 ,birthday datetime  null 
 9 ,addresss text  null 
10 ,monyes decimal(19,4)  null 
11 ,smonyes decimal(10,4)  null 
12 ,nums int  null 
13 ,moneys decimal(19,4)  null 
14 ,smo decimal(10,4)  null 
15 ,curversion timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP  not null 
16 , primary key (id,phone) );   
17 CREATE   INDEX IX_name ON tbtest( name );
18 CREATE   INDEX IX_phone_age ON tbtest( phone,age );
19 CREATE   INDEX IX_nums ON tbtest( nums );
20 CREATE   INDEX IX_bir_nam_pho ON tbtest( birthday,name,phone );
 
    在mysql中創建正常,查看mysql的建表腳本如下:
 
 

5 存儲過程腳本

    SQL SERVER轉換MySQL表結構及索引的腳本如下:
 
-- =============================================
-- Author: suxinyu
-- Create date: 20170612
-- Description: 根據表名自動把表格的所有建表DDL SQL轉化為 MySQL的建表SQL,不包含分區表,不處理區域數據類型;執行過程中,需要把存儲過程建立在需要導出的數據庫中。
-- Example: exec p_tb_mssqltomysql 'orders,ordernums,channels'
-- =============================================
--存儲過程建立在需要導出表結構的DB上

USE db
GO

CREATE PROC [dbo].[p_tb_mssqltomysql] @tbsql varchar(1000) AS SET NOCOUNT ON ; --處理tablename的字符串,把tablename字符串分割成每一行存儲進入表變量中 DECLARE @tab_tablename table(tbname varchar(100)) DECLARE @tbname varchar(100) INSERT INTO @tab_tablename(tbname) SELECT SUBSTRING(@tbsql,NUMBER,CHARINDEX(',',@tbsql+',',NUMBER)-number) FROM master.dbo.spt_values WHERE TYPE='P' AND number>0 AND SUBSTRING(','+@tbsql,number,1)=',' --把mysql跟mssql的數據類型對應起來存儲 --空間數據類型不處理 --money類型處理為float --timestamp處理為 timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP DECLARE @tbtype table(mssql varchar(20),mysql varchar(20)) INSERT INTO @tbtype(mssql,mysql) values( 'bigint','bigint'),('binary','binary'),('binary','binary'),('bit','tinyint'),('char','char'),('date','date'),('datetime','datetime'),('datetime2','datetime'),('datetimeoffset','datetime'),('decimal','decimal'),('float','float'),('int','int'),('money','decimal'),('nchar','char'),('ntext','text'),('numeric','decimal'),('nvarchar','varchar'),('real','float'),('smalldatetime','datetime'),('smallint','smallint'),('smallmoney','decimal'),('text','text'),('time','time'),('timestamp','timestamp'),('tinyint','tinyint'),('uniqueidentifier','varchar(40)'),('varbinary','varbinary'),('varchar','varchar'),('xml','text') DECLARE @tb_exec_sql table(tbname varchar(100),sql nvarchar(max),indexs nvarchar(max)) DECLARE @indexs_sql nvarchar(max) --轉化表格SQL DECLARE NAME CURSOR FOR SELECT tbname FROM @tab_tablename OPEN NAME FETCH NEXT FROM name INTO @tbname WHILE @@FETCH_STATUS =0 BEGIN ;WITH data AS ( SELECT case when b.is_unique=1 then ' UNIQUE ' else ' ' end is_unique, OBJECT_NAME(A.OBJECT_ID) obj_name, COL_NAME(A.object_id,A.column_id) colname, SUBSTRING(COL_NAME(A.object_id,A.column_id),1,3) col_short, is_included_column, index_column_id, a.index_id, A.OBJECT_ID FROM SYS.index_columns A INNER JOIN SYS.INDEXES B ON A.OBJECT_ID=B.OBJECT_ID AND A.index_id=B.index_id WHERE b.type!=1 and OBJECT_NAME(A.OBJECT_ID)=@tbname ) SELECT @indexs_sql= REPLACE( (STUFF( ( SELECT ' CREATE ' + a.is_unique +' INDEX ' + CASE WHEN COUNT(*) >=3 THEN SUBSTRING(('IX_'+stuff((SELECT '_'+col_short FROM data b where a.object_id=b.object_id and a.index_id=b.index_id FOR XML PATH('')),1,1,'')),1,64) ELSE 'IX_'+stuff((SELECT '_'+colname FROM data b where a.object_id=b.object_id and a.index_id=b.index_id FOR XML PATH('')),1,1,'') END +' ON ' + a.obj_name +'( ' + stuff((SELECT ','+colname FROM data b where a.object_id=b.object_id and a.index_id=b.index_id FOR XML PATH('')),1,1,'') +' ); ' FROM data a GROUP BY a.is_unique,a.obj_name,a.object_id,a.index_id ORDER BY a.object_id,a.index_id FOR XML PATH('') ),1,1,'') ),'&#x0D;','') INSERT INTO @tb_exec_sql(tbname,indexs,sql) SELECT @tbname,@indexs_sql, 'CREATE TABLE '+@tbname+'('+ REPLACE( STUFF( ( SELECT ','+a.name + case when b.name = 'timestamp' then ' timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ' when b.name = 'uniqueidentifier' then ' varchar(40) ' when b.name in ('char','nchar','nvarchar','varbinary','varchar') then ( case when a.length<0 then ' text ' else ' '+c.mysql+'('+ (case when b.name like 'n%' then cast(a.length/2 as varchar(10)) else cast(a.length as varchar(10)) end )+')' end ) when b.name in ('decimal','float','money','numeric','smallmoney') then ' '+c.mysql+'('+ cast(a.prec as varchar(10)) +','+ cast(a.scale as varchar(10)) +') ' else ' '+c.mysql+' ' end + case when a.isnullable=0 then ' not null ' else ' null ' end + case when COLUMNPROPERTY( A.ID,A.NAME,'ISIDENTITY')=1 then ' auto_increment ' else '' end + case when a.length<0 or b.name in ('text') then ' ' when e.text like ' ((%' then ' default '+substring(e.text,3,len(e.text)-4) when e.text like ' (''%' then ' default '+substring(e.text,2,len(e.text)-2) else ' ' end + ISNULL(' comment "'+cast(g.value as varchar(1000))+'" ',' ') FROM sys.syscolumns A LEFT JOIN sys.systypes B ON A.XUSERTYPE=B.XUSERTYPE LEFT JOIN @tbtype C ON b.name collate Chinese_PRC_CI_AS = c.mssql LEFT JOIN sys.sysobjects D ON A.ID=D.ID AND D.XTYPE='U' AND D.NAME<>'DTPROPERTIES' LEFT JOIN sys.syscomments E ON A.CDEFAULT=E.ID LEFT JOIN sys.extended_properties G ON A.ID=G.major_id AND A.COLID=G.minor_id WHERE D.NAME =@tbname order by a.colid FOR XML PATH('') ),1,1,'') ,'&#x0D;','') + ISNULL( ( SELECT ', primary key ('+STUFF( ( SELECT ','+col_name(i.object_id,ik.column_id) FROM sys.indexes i JOIN sys.index_columns ik ON i.index_id=ik.index_id and i.object_id=ik.object_id WHERE i.type=1 and i.object_id=object_id(@tbname) ORDER BY key_ordinal FOR XML PATH('') ),1,1,'') +') ' ) ,'') + ')' + ISNULL( ( SELECT ' COMMENT "'+CAST(value AS VARCHAR(1000))+'"; ' FROM sys.extended_properties where major_id=object_id(@tbname) and minor_id=0 ),';') FETCH NEXT FROM NAME INTO @tbname END CLOSE NAME DEALLOCATE NAME SELECT * FROM @tb_exec_sql

 

 


免責聲明!

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



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