IF EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID('[dbo].[mps_gen_alter_table]') AND type = 'P')
DROP PROCEDURE [dbo].[mps_gen_alter_table]
GO
-- =============================================
-- Author:  flobit
-- Create date: May  2 2018  2:45PM
-- Description: 根据mps_fieldinfo的字段取对比物理表中的字段信息,然后生成调整对应表的SQL语句
-- =============================================
CREATE PROCEDURE [dbo].[mps_gen_alter_table] 
@cmpid int,
@tb_name varchar(256),
@sql varchar(max)='' out,
@log bit=0
AS
BEGIN
SET NOCOUNT ON;
DECLARE @object_id bigint=OBJECT_ID(@tb_name)
declare @db_name varchar(32) --数据库名称
declare @pure_db_name varchar(32)         --数据库名称,不包含.dbo.
declare @sql_get_pysicalfieldinfo nvarchar(max)='' --存储获取物理表字段字段信息的SQL
declare @index int = CHARINDEX('.dbo.',@tb_name);
if @index > 1
begin
set @db_name = SUBSTRING(@tb_name,1,@index+4);
set @tb_name = SUBSTRING(@tb_name,@index+5,LEN(@tb_name));
end else begin
set @db_name = DB_NAME()+'.dbo.'
end
set @pure_db_name=REPLACE(@db_name,'.dbo.','');
--获取目标字段信息
CREATE TABLE #target_fieldinfo(fldi_id int)
--添加临时表的字段
exec mps_add_targetfieldinfo_field
--获取并格式化字段
exec mps_normalize_targetfieldinfo @cmpid,@db_name,@tb_name
--创建临时表用来存储物理表中的字段信息
CREATE TABLE #physical_fieldinfo(
[fldi_fieldname] [varchar](50) ,
[fldi_datatype] [varchar](10) ,
[fldi_size] [varchar](10),
[fldi_decimal] [int] ,
[fldi_nlzdatatype] [varchar](64) ,
[fldi_default_constraint] [varchar](128)
)
-----------------------------获取物理表字段信息---------------------------------
set @sql_get_pysicalfieldinfo='
insert into #physical_fieldinfo(fldi_fieldname,fldi_datatype,fldi_size,fldi_decimal,fldi_default_constraint)
select _columns.name,_types.name as type,
case when _types.name in (''decimal'',''numeric'') then _columns.precision 
 when _types.name in (''nchar'',''nvarchar'') then _columns.max_length/2 else _columns.max_length 
 end as size,
case when _types.name in (''decimal'',''numeric'') then _columns.scale  else 0 
 end as decimal,
_constraints.name as default_constraint

from      (select column_id,name,scale,precision,max_length,user_type_id from '+@pure_db_name+'.sys.columns where object_id='+CONVERT(VARCHAR,@object_id)+') AS _columns 
left join (select name,parent_object_id,parent_column_id from '+@pure_db_name+'.sys.default_constraints where parent_object_id='+CONVERT(VARCHAR,@object_id)+') as  _constraints 
on _columns.column_id=_constraints.parent_column_id,'+@pure_db_name+'.sys.types _types
where _columns.user_type_id=_types.user_type_id'

exec sp_executesql @sql_get_pysicalfieldinfo
update #physical_fieldinfo set fldi_size='MAX' where (fldi_datatype='varchar' or fldi_datatype='nvarchar')  and fldi_size = '-1'
update #physical_fieldinfo set fldi_nlzdatatype=fldi_datatype
update #physical_fieldinfo set fldi_nlzdatatype=fldi_datatype+'('+CONVERT(VARCHAR,fldi_size)+')' where fldi_datatype in ('char','nchar','varchar','nvarchar')
update #physical_fieldinfo set fldi_nlzdatatype=fldi_datatype+'('+CONVERT(VARCHAR,fldi_size)+','+CONVERT(VARCHAR,fldi_decimal)+')' where fldi_datatype in ('decimal','numeric')

if(@log=1)
begin
select * from #target_fieldinfo
select * from #physical_fieldinfo
end


--处理增删改操作
set @sql  = ''
--处理新增字段
select @sql+='alter table '+@db_name+@tb_name+' add ' 
--字段名
+ fldi_fieldname + ' '
--字段类型
+fldi_nlzdatatype
--默认值
+ ' default ('+fldi_default+')'
+char(13)+char(10)+'GO'+char(13)+char(10)
+'update '+@db_name+@tb_name+' set '+fldi_fieldname+' = '+fldi_default+' where '+fldi_fieldname+' IS NULL'+char(13)+char(10)
+char(13)+char(10)
from #target_fieldinfo 
where fldi_fieldname IS NOT NULL and fldi_fieldname!='' and fldi_fieldname not in (select fldi_fieldname from #physical_fieldinfo)
and fldi_oldname=''
--处理新增时要注意,为了防止已经存在的记录出现null值应该将新增字段update 为默认值


--处理删除字段
--先删除默认值约束,然后再删除字段
--当fldi_fieldname='' 并且fldi_oldname='xxx'的时候表示删除xxx这个字段
select @sql+=case when fldi_default_constraint IS NOT NULL 
then 'alter table '+@db_name+@tb_name+' drop '+fldi_default_constraint +char(13)+char(10) +'alter table '+@tb_name+' drop column '+physic_table.fldi_fieldname
else 'alter table '+@db_name+@tb_name+' drop column '+physic_table.fldi_fieldname end 
+char(13)+char(10)
from #target_fieldinfo target_table,#physical_fieldinfo physic_table
where (target_table.fldi_fieldname='' or target_table.fldi_fieldname is not null)
and target_table.fldi_oldname!='' and target_table.fldi_oldname is not null
and physic_table.fldi_fieldname=target_table.fldi_oldname and target_table.fldi_fieldname=''

--处理修改字段,就认为是 字段名称,字段类型,长度,缺省值都修改了
select @sql+=
--如果字段名称修改了
case when _target.fldi_fieldname!=_physical.fldi_fieldname and _target.fldi_oldname=_physical.fldi_fieldname then 
'sp_rename '''+@db_name+@tb_name+'.'+_physical.fldi_fieldname+''','''+_target.fldi_fieldname+''',''COLUMN''' else '' end
+char(13)+char(10)+
--如果字段类型被修改,1 先删除缺省值约束,我懒得判断该不该删除了,直接删除,最后再创建
case when _target.fldi_datatype != _physical.fldi_datatype or _target.fldi_size != _physical.fldi_size or _target.fldi_decimal!= _physical.fldi_decimal then 
--删除缺省值约束
case when _physical.fldi_default_constraint IS NOT NULL then 'alter table '+@db_name+@tb_name+' drop '+_physical.fldi_default_constraint+char(13)+char(10)  else '' end +
--生成修改字段类型和长度,这里可能需要做异常处理,如果修改字段不成功则将字段重新命名,创建字段
'begin try'+char(13)+char(10)+
'alter table ' +@db_name+ @tb_name +' alter column ' +_target.fldi_fieldname+ ' '+_target.fldi_nlzdatatype+char(13)+char(10)+
'end try'+char(13)+char(10)+
'begin catch'+char(13)+char(10)+
'exec sp_rename '''+@db_name+@tb_name+'.'+_physical.fldi_fieldname+''','''+_physical.fldi_fieldname+'_bak'',''COLUMN''' +char(13)+char(10)+
'alter table '++@db_name+@tb_name+' add '+_target.fldi_fieldname+' '+_target.fldi_nlzdatatype+char(13)+char(10)+
'end catch'+char(13)+char(10)+
 --添加缺省值
'alter table '+@db_name+@tb_name+' add default('+_target.fldi_default+') for '+_target.fldi_fieldname +char(13)+char(10)+
 --跟新缺省值
'update '+@db_name+@tb_name+' set '+_target.fldi_fieldname+' = '+_target.fldi_default+' where '+_target.fldi_fieldname+' IS NULL'+char(13)+char(10)
 else '' end
 +'GO'+char(13)+char(10)

from #target_fieldinfo _target,#physical_fieldinfo _physical
where (_target.fldi_fieldname=_physical.fldi_fieldname or _target.fldi_oldname=_physical.fldi_fieldname)
and (_target.fldi_fieldname != _physical.fldi_fieldname
or _target.fldi_nlzdatatype != _physical.fldi_nlzdatatype )
--select @sql as Text
END