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,根据实例的设置来处理
 
IDSQL SERVERMySQLDescription
1bigintbigint 
2binarybinary 
3bittinyintSQL SERVER的bit类型,对于零,识别为False,非零值识别为True。
MySQL中没有指定的bool类型,一般都使用tinyint来代替
4charchar 
5datedate 
6datetimedatetime注意,mssql的保留到微秒(秒后小数点3位),而mysql仅保留到秒
7datetime2datetime注意,mssql的保留到微秒(秒后小数点7位),而mysql仅保留到秒
8datetimeoffsetdatetime注意,mssql的保留时区,这个需要程序自己转换
mssql的保留到微秒(秒后小数点7位),而mysql仅保留到秒
9decimaldecimal 
10floatfloat 
11intint 
12moneyfloat默认转换为decimal(19,4)
13ncharcharSQL SERVER转MySQL按正常字节数转就可以
14ntexttext 
15numericdecimal 
16nvarcharvarchar 
17realfloat 
18smalldatetimedatetime 
19smallintsmallint 
20smallmoneyfloat默认转换为decimal(10,4)
21texttext 
22timetime注意,mssql的保留到秒后小数点8位,而mysql仅保留到秒
23timestamptimestamp注意,mssql的行时间戳,处理为mysql的 timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 。这会对后面导数据造成影响,从功能方面来看,可以按照上文转换;如果从数据来看,若需要转换16进制的文字存储到mysql中,则这里设置为bigint即可,在表格中的临时表中设置。
24tinyinttinyint 
25uniqueidentifiervarchar(40)对应mysql的UUID(),设置为文本类型即可。
26varbinaryvarbinary 
27varcharvarchar 
28xmltextmysql不支持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

 

 

转载于:https://www.cnblogs.com/xinysu/p/6992415.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值