【C#】编号生成器(定义单号规则、固定字符、流水号、业务单号)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129129787
【C#】日期范围生成器(开始日期、结束日期)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129040663
【C#】IIS平台下,WebAPI发布及异常处理
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126539836
【C#】MySQL数据库导入工具(批量Excel插入)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126427323
【C#】简单二维码制作和打印工具
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126884228
【C#】最全单据打印(打印模板、条形码&二维码、字体样式、项目源码)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129415723
目录
系列文章
前言
一、问题描述
二、解决方案
2.1 设置功能
2.2 校验工具
三、软件开发(源码)
3.1 数据库脚本
3.2 循环取所有表
3.3 获取表SQL
四、项目展示
我能抽象出整个世界,但是我不能抽象你。 想让你成为私有常量,这样外部函数就无法访问你。 又想让你成为全局常量,这样在我的整个生命周期都可以调用你。 可惜世上没有这样的常量,我也无法定义你,因为你在我心中是那么的具体。
哈喽大家好,本专栏为【项目实战】专栏,有别于【底层库】专栏,我们可以发现增加 了『问题描述』、『项目展示』章节,十分符合项目开发流程,让读者更加清楚项目解决的问题、以及产品能够达到的效果。本专栏收纳项目开发过程的解决方案,是我项目开发相对成熟、可靠方法的提炼,我将这些问题的解决思路梳理,撰写本文分享给大家,大家遇到类似问题,可按本文方案处理。
本专栏会持续更新,不断完善,专栏文章关联性较弱(文章之间依赖性较弱,没有阅读顺序)。大家有任何问题,可以私信我。如果您对本专栏感兴趣,欢迎关注吧,我将带你用最简洁的代码,实现复杂的功能。
为方便公司对数据库对象进行设计、管理、监控、记录,决定舍弃使用ssms对数据进行变更,而内部推广公司自主开发的数据库设计平台(目前测试版已上线)。但与此同时,产生了另一个问题就是数据库对象不一致问题,因为“数据库设计平台”是搭建服务器126上(这个暂不解释原因,其下文称为“工具数据库”),而实际的数据库连接在231上称为“实际数据库”),这里我们就需要做一个工具,可以校验到两个存放在不同数据库下的数据表(列字段、pk、索引、字段类型、长度、默认值等)、视图、存储过程之间的差异。让产品经理决定如何经行一致性的变更。
运行的效果展示:
设置“实际数据库”连接实例,工具默认运行实例数据库已经代码默认。
目前实现表校验,日后准备集成近外壳分页下
关键代码的讲解:
我们从工具数据库、实际数据库获取所有的表,并且要合并去重,因为可能两边的数据库表都有缺失的可能性。这里我书写了一个存储过程实现,传入参数:数据库类型,类别,连接串,具体的base。
/*
功能:获取所有表
创建人:gyc
创建时间:2019年7月18日14:32:39
*/
CREATE Procedure [dbo].[P_CheckUniformityForTable] (@DBTypeNo varchar(500),@DBNo varchar(5000),@devconnestring varchar(500),@tbwsbase varchar(500))
as
---检查表
--declare @DBTypeNo varchar(40)
--select @DBTypeNo = 'TB0719'
--declare @devconnestring varchar(5000)
--declare @tbwsbase varchar(500)
--declare @DBNo varchar(500)
--select @devconnestring = 'Data Source=202.101.103.251\test; Database=; Uid=; Pwd='
--select @tbwsbase = 'wsbase000'
--select @DBNo = 'base'
declare @result table(error_msg varchar(8000),table_name varchar(8000),col_name varchar(8000),devconnet varchar(8000),dbtype_no varchar(8000))
declare @TBAllTableName table(same_tag varchar(10),table_name varchar(8000),db_guid varchar(8000),detail_note varchar(8000),id int)
declare @tbsourceTableList table(table_name varchar(5000))--工具表
declare @tbtrueTableList table(table_name varchar(5000))--真表
insert into @tbsourceTableList(table_name)
select tbl_name
from tbl_list main
where main.dbtype_no =@DBTypeNo
and main.db_no=@DBNo
and main.del_tag='F'
declare @strSQL varchar(8000)
select @strSQL = 'SELECT name FROM OPENDATASOURCE(
''SQLOLEDB'',
'''+@devconnestring+'''
).'+@tbwsbase+'.sys.tables
where (
name <>''sysdiagrams''
and name not like ''temp%''
and name not like ''%temp''
and name not like ''tmp%''
) and not exists(select top 1 1 from [tbl_list] tl where tl.dbtype_no='''+@DbtypeNo+''' and tl.db_no = '''+@DBNo+''' and tl.tbl_name=name)
order by name
'
print @strSQL
insert into @tbtrueTableList
(table_name)
exec(@strSQL)
--------------------------存储所有表名
insert into @TBAllTableName
(same_tag,table_name,detail_note)
select 'F',true.table_name,''
from @tbtrueTableList true
union all
select 'F',mgr.table_name,''
from @tbsourceTableList mgr
select same_tag,table_name,detail_note,
CAST(ROW_NUMBER()over(order by table_name) as int)as id
from @TBAllTableName main
循环取出的所有表,根据表名,分别取“工具数据库”、“实际数据库”中的表、字段、索引元素。
//初始化取出所有校验数据
void InitFetchCheckData()
{
m_dtSource = new DataTable();
m_dtAim = new DataTable();
m_dtSourceCol = new DataTable();
m_dtAimCol = new DataTable();
m_dtSourcePKIdx = new DataTable();
m_dtAimPKIdx = new DataTable();//字段约束
//表
string l_strSql = "select tbl_name from tbl_list "
+ "where dbtype_no=" + cds_Search.dbtype_no.QuotedStr();
m_dtSource = GetFetchDataOrder(l_strSql,false,"id");
l_strSql = Const.ct_FetchDBTableSql;
FetchDataC l_fetch=new FetchDataC(cds_Search.dev_connetstring);
l_fetch.InitSQL(l_strSql,false);
if(l_fetch.Fetch())
{
m_dtAim=l_fetch.Data.Tables[0];
}
//字段
l_strSql = Const.sql_fetchColList+" and tl.dbtype_no="+cds_Search.dbtype_no.QuotedStr()
+ " and tl.db_no="+cds_Search.db_no.QuotedStr();
m_dtSourceCol = GetFetchDataOrder(l_strSql,false,"order_sn");
l_strSql = string.Format(Const.ct_FetchDBColSql,cds_Search.dbtype_no.QuotedStr());
FetchDataC l_fetchcol=new FetchDataC(cds_Search.dev_connetstring);
l_fetchcol.InitSQL(l_strSql,false);
if(l_fetchcol.Fetch())
{
m_dtAimCol=l_fetchcol.Data.Tables[0];
}
//pk
l_strSql = Const.sql_fetchPKIndexList + " and tl.dbtype_no="+cds_Search.dbtype_no.QuotedStr();
m_dtSourcePKIdx = GetFetchDataOrder(l_strSql,false,"tbl_name");
l_strSql = Const.ct_FetchDBIndexSql;
l_fetch=new FetchDataC(cds_Search.dev_connetstring);
l_fetch.InitSQL(l_strSql,false);
if(l_fetch.Fetch())
{
m_dtAimPKIdx=l_fetch.Data.Tables[0];
}
}
以上代码封装过了,可能没多大的参考价值,下面简略说一下思想。
这里“工具数据库”表的存放在tbl_list、列字段存放在tbl_col、索引存放在tbl_idx表里面,如果你们没有创建的对应的存放表也没关系可以通过系统表来获取。
“实际数据库”根据连接串(数据库连接实例),从系统表中获取元素。
select tb.name [tbl_name]
from sys.tables tb
where (
name <>'sysdiagrams'
and name not like 'temp%'
and name not like '%temp'
and name not like 'tmp%')
可能有些人要晕了,前言不是说了“数据库设计平台(工具)”是搭建服务器126上,而“实际数据库”在236上,那我在126上执行这个SQL怎么能得到236的数据库表呢?其实刚刚的代码
FetchDataC(cds_Search.dev_connetstring); cds_Search.dev_connetstring就是数据库连接串这里是进行代码封装的,其原理等同于我写的存储过程
这一段脚本,用“OPENDATASOURCE”可以进行跨库查询
2)取“实际数据库”的列字段,包含字段类型、长度、默认值等
select col.name as col_name,
(st.name) as data_type,
(col.max_length) char_len,
case when col.is_identity=1 then 'T' else 'F' end as identity_tag,
case when col.is_nullable=1 then 'T' else 'F' end as null_tag,
ISNULL(dc.definition, '') as default_value,
tb.name as tbl_name
from sys.columns col
inner join sys.tables tb on tb.object_id = col.object_id
inner join sys.types st on st.system_type_id = col.system_type_id and st.name <> 'sysname'
left join sys.DEFAULT_CONSTRAINTS dc on dc.PARENT_OBJECT_ID = col.object_id and dc.PARENT_COLUMN_ID = col.column_id
order by tb.name,col.name
select i.name as idx_name,
(case when is_primary_key = 0 then 'F' else 'T' end) as pk_tag,
t.name as tbl_name
from sys.indexes i
inner join sys.tables t on t.object_id = i.object_id
where i.type<>0 order by t.name,i.name, i.index_id
3、“工具数据库”、“实际数据库”取出来的数据放进定义的DataTable变量中,后面,为减少与数据库的交互次数,提升效率,我们一次性取出base下全部数据,然后循环“表名”过滤数据,然后就写算术代码的匹配规则了,一样的检测为打钩“√”,不一样不勾选。右键可以具体查看哪些表字段、、索引属性不一样。
#region 校验
public void CheckData()
{
int l_cnt = cds_Detail.DataTable.Rows.Count;
m_intCnt = 0;
m_intPos = 0;
m_intCnt = l_cnt;
int l_pos = 0;
FormShow l_frm = new FormShow(this);
l_frm.StartPosition = FormStartPosition.CenterScreen;
l_frm.Show(m_FormMain);
l_frm.ShowMainTitle("...");
l_frm.UpdateMainProgress(l_pos*100/l_cnt);
Application.DoEvents();
InitFetchCheckData();
foreach(DataRow drDetail in cds_Detail.DataTable.Rows)
{
l_pos++;
m_intPos = l_pos;
if (l_frm.m_IsStop)
{
m_intCnt = 0;
m_intPos = 0;
break;
}
l_frm.ShowMainTitle(drDetail["table_name"].ToString());
l_frm.UpdateMainProgress(l_pos*100/l_cnt);
Application.DoEvents();
if(!CheckTable(drDetail)) continue;
if(!CheckCol(drDetail)) continue;
if(!CheckPKidx(drDetail)) continue;
drDetail["same_tag"] = "T";
}
l_frm.Close();
cds_Detail.Post();
}
public bool IsGetData()
{
if(m_intPos < m_intCnt)
{
return false;
}
return true;
}
//初始化取出所有校验数据
void InitFetchCheckData()
{
m_dtSource = new DataTable();
m_dtAim = new DataTable();
m_dtSourceCol = new DataTable();
m_dtAimCol = new DataTable();
m_dtSourcePKIdx = new DataTable();
m_dtAimPKIdx = new DataTable();//字段约束
//表
string l_strSql = "select tbl_name from tbl_list "
+ "where dbtype_no=" + cds_Search.dbtype_no.QuotedStr();
m_dtSource = GetFetchDataOrder(l_strSql,false,"id");
l_strSql = Const.ct_FetchDBTableSql;
FetchDataC l_fetch=new FetchDataC(cds_Search.dev_connetstring);
l_fetch.InitSQL(l_strSql,false);
if(l_fetch.Fetch())
{
m_dtAim=l_fetch.Data.Tables[0];
}
//字段
l_strSql = Const.sql_fetchColList+" and tl.dbtype_no="+cds_Search.dbtype_no.QuotedStr()
+ " and tl.db_no="+cds_Search.db_no.QuotedStr();
m_dtSourceCol = GetFetchDataOrder(l_strSql,false,"order_sn");
l_strSql = string.Format(Const.ct_FetchDBColSql,cds_Search.dbtype_no.QuotedStr());
FetchDataC l_fetchcol=new FetchDataC(cds_Search.dev_connetstring);
l_fetchcol.InitSQL(l_strSql,false);
if(l_fetchcol.Fetch())
{
m_dtAimCol=l_fetchcol.Data.Tables[0];
}
//pk
l_strSql = Const.sql_fetchPKIndexList + " and tl.dbtype_no="+cds_Search.dbtype_no.QuotedStr();
m_dtSourcePKIdx = GetFetchDataOrder(l_strSql,false,"tbl_name");
l_strSql = Const.ct_FetchDBIndexSql;
l_fetch=new FetchDataC(cds_Search.dev_connetstring);
l_fetch.InitSQL(l_strSql,false);
if(l_fetch.Fetch())
{
m_dtAimPKIdx=l_fetch.Data.Tables[0];
}
}
//表
public bool CheckTable(DataRow a_dr)
{
if(m_dtSource!=null && m_dtAim!=null)
{
if(m_dtSource.Select("tbl_name="+a_dr["table_name"].ToString().QuotedStr()).Length<=0)
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "工具表缺失";
a_dr.Table.AcceptChanges();
return false;
}
if(m_dtAim.Select("tbl_name="+a_dr["table_name"].ToString().QuotedStr()).Length<=0)
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "工具表缺失";
a_dr.Table.AcceptChanges();
return false;
}
}
return true;
}
//字段
public bool CheckCol(DataRow a_dr)
{
if(m_dtSourceCol!=null && m_dtAimCol!=null)
{
DataTable l_dtSourceCol = m_dtSourceCol.SelectToTable("tbl_name="+a_dr["table_name"].ToString().QuotedStr(),"");
DataTable l_dtAimCol = m_dtAimCol.SelectToTable("tbl_name="+a_dr["table_name"].ToString().QuotedStr(),"");
foreach(DataRow dr in l_dtAimCol.Rows)
{
bool isexists = false;
string l_strdefault_value1,l_strdefault_value2;
foreach(DataRow sdr in l_dtSourceCol.Rows)
{
if(dr["col_name"].ToString().SameText(sdr["col_name"].ToString()))
{
isexists = true;
//类型
if(!dr["data_type"].ToString().SameText(sdr["data_type"].ToString()))
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "表字段类型不一致";
a_dr.Table.AcceptChanges();
return false;
}
//长度
if(!dr["char_len"].ToString().SameText(sdr["char_len"].ToString()))
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "表字段长度不一致";
a_dr.Table.AcceptChanges();
return false;
}
//默认值
if(!VarcharCompare(dr["data_type"].ToString(),sdr["default_value"].ToString(),
dr["default_value"].ToString(),out l_strdefault_value1,out l_strdefault_value2))
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "表字段默认值不一致";
a_dr.Table.AcceptChanges();
return false;
}
}
}
if(!isexists)
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "工具表字段缺失";
a_dr.Table.AcceptChanges();
return false;
}
}
foreach(DataRow dr in l_dtSourceCol.Rows)
{
bool isexists = false;
foreach(DataRow sdr in l_dtAimCol.Rows)
{
if(dr["col_name"].ToString().SameText(sdr["col_name"].ToString()))
{
isexists = true;
}
}
if(!isexists)
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "数据库表字段缺失";
a_dr.Table.AcceptChanges();
return false;
}
}
}
return true;
}
//pk
public bool CheckPKidx(DataRow a_dr)
{
if(m_dtAimPKIdx!=null&&m_dtSourcePKIdx!=null)
{
DataTable l_dtAimPKIdx = m_dtAimPKIdx.SelectToTable("tbl_name="+a_dr["table_name"].ToString().QuotedStr(),"");
DataTable l_dtSourcePKIdx = m_dtSourcePKIdx.SelectToTable("tbl_name="+a_dr["table_name"].ToString().QuotedStr(),"");
foreach(DataRow dr in l_dtAimPKIdx.Rows)
{
bool isexists = false;
foreach(DataRow sdr in l_dtSourcePKIdx.Rows)
{
if(dr["idx_name"].ToString().SameText(sdr["idx_name"].ToString()))
{
isexists = true;
//比较pk
if(dr["pk_tag"].ToString().SameText("T"))
{
if(!dr["pk_tag"].ToString().SameText(sdr["pk_tag"].ToString()))
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "表PK不一致";
a_dr.Table.AcceptChanges();
return false;
}
}
else
{
if(!dr["pk_tag"].ToString().SameText(sdr["pk_tag"].ToString()))
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "表索引不一致";
a_dr.Table.AcceptChanges();
return false;
}
}
}
}
if(!isexists)
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "工具表索引缺失";
a_dr.Table.AcceptChanges();
return false;
}
}
foreach(DataRow dr in m_dtSourcePKIdx.Select("tbl_name="+a_dr["table_name"].ToString().QuotedStr()))
{
bool isexists = false;
foreach(DataRow sdr in m_dtAimPKIdx.Select("tbl_name="+a_dr["table_name"].ToString().QuotedStr()))
{
if(dr["idx_name"].ToString().SameText(sdr["idx_name"].ToString()))
{
isexists = true;
}
}
if(!isexists)
{
a_dr["same_tag"] = "F";
a_dr["detail_note"] = "实际表索引缺失";
a_dr.Table.AcceptChanges();
return false;
}
}
}
return true;
}
//默认值的比较(明明数据默认值不对,还让程序特殊处理mmp)
bool VarcharCompare(string a_strDataType,string a_strValue1,string a_strValue2,
out string a_strNewValue1,out string a_strNewValue2)
{
bool a_bSame = false;
if(a_strDataType.Contains("varchar"))
{
string l_temp1,l_temp2;
l_temp1 = a_strValue1.Replace("(","");
l_temp1 = l_temp1.Replace(")","");
l_temp1 = l_temp1.Replace("'","");
l_temp2 = a_strValue2.Replace("(","");
l_temp2 = l_temp2.Replace(")","");
l_temp2 = l_temp2.Replace("'","");
a_bSame = l_temp1.SameText(l_temp2);
a_strNewValue1 = l_temp1;
a_strNewValue2 = l_temp2;
}
else if(a_strDataType.Contains("int"))
{
string l_temp1,l_temp2;
l_temp1 = a_strValue1.Replace("(","");
l_temp1 = l_temp1.Replace(")","");
l_temp1 = l_temp1.Replace("'","");
l_temp2 = a_strValue2.Replace("(","");
l_temp2 = l_temp2.Replace(")","");
l_temp2 = l_temp2.Replace("'","");
if((l_temp1.SameText(l_temp2))
|| (l_temp1.SameText("0")&&l_temp2.SameText(""))
|| (l_temp2.SameText("0")&&l_temp1.SameText("")))
{
a_bSame = true;
}
else
{
a_bSame = false;
}
a_strNewValue1 = l_temp1;
a_strNewValue2 = l_temp2;
}
else if(a_strDataType.Contains("float"))
{
string l_temp1,l_temp2;
l_temp1 = a_strValue1.Replace("(","");
l_temp1 = l_temp1.Replace(")","");
l_temp1 = l_temp1.Replace("'","");
l_temp2 = a_strValue2.Replace("(","");
l_temp2 = l_temp2.Replace(")","");
l_temp2 = l_temp2.Replace("'","");
if((l_temp1.SameText(l_temp2))
|| (l_temp1.SameText("0")&&l_temp2.SameText(""))
|| (l_temp2.SameText("0")&&l_temp1.SameText("")))
{
a_bSame = true;
}
else
{
a_bSame = false;
}
a_strNewValue1 = l_temp1;
a_strNewValue2 = l_temp2;
}
else if(a_strDataType.Contains("datetime"))
{
string l_temp1,l_temp2;
l_temp1 = a_strValue1.Replace("(getdate())","getdate()");
l_temp2 = a_strValue2.Replace("(getdate())","getdate()");
l_temp1 = a_strValue1.Replace("('')","");
l_temp2 = a_strValue2.Replace("('')","");
a_bSame = string.Equals( l_temp1,l_temp2);
a_strNewValue1 = l_temp1;
a_strNewValue2 = l_temp2;
}
else
{
a_bSame = a_strValue1.SameText(a_strValue2);
a_strNewValue1 = a_strValue1;
a_strNewValue2 = a_strValue2;
}
return a_bSame;
}
#endregion
其中VarcharCompare()是自己写的方法,为了比较默认值的问题的,主要原因是采用“系统表”获取的方式默认值常会得到:(‘’)、(‘0’)、(getdate())这样的东西,但“工具数据库”直接取得tbl_col表中的数据所以不存在这种格式问题,故而要经行特殊出来。
4、右键查看详细信息的处理代码
#region 查询列
public void FetchColList(string table_name)
{
cds_col.Clear();
cds_colDataType.Clear();//数据类型
cds_colCharLen.Clear();//长度
cds_colDefaultValue.Clear();//默认值
DataTable l_dtsourceCol = m_dtSourceCol.SelectToTable("tbl_name="+table_name.QuotedStr(),"");
DataTable l_dtaimCol = m_dtAimCol.SelectToTable("tbl_name="+table_name.QuotedStr(),"");
if(l_dtsourceCol!=null&&l_dtaimCol!=null)
{
foreach(DataRow dr in l_dtaimCol.Rows)
{
bool isexists = false;
string l_temp1 = "";
string l_temp2 = "";
foreach(DataRow sdr in l_dtsourceCol.Rows)
{
if(dr["col_name"].ToString().SameText(sdr["col_name"].ToString()))
{
isexists = true;
//比较列
if(!dr["data_type"].ToString().SameText(sdr["data_type"].ToString()))
{
cds_colDataType.Append();
cds_colDataType.col_name = dr["col_name"].ToString();
cds_colDataType.field1 = sdr["data_type"].ToString();
cds_colDataType.field2 = dr["data_type"].ToString();
}
//长度
if(!dr["data_type"].ToString().SameText("text")
|| !dr["data_type"].ToString().SameText("image") )
{
if(!dr["char_len"].ToString().SameText(sdr["char_len"].ToString()))
{
cds_colCharLen.Append();
cds_colCharLen.col_name = dr["col_name"].ToString();
cds_colCharLen.data_type = dr["data_type"].ToString();
cds_colCharLen.field1 = sdr["char_len"].ToString();
cds_colCharLen.field2 = dr["char_len"].ToString();
}
}
if(!VarcharCompare(dr["data_type"].ToString(),sdr["default_value"].ToString(),
dr["default_value"].ToString(),out l_temp1,out l_temp2))
{
cds_colDefaultValue.Append();
cds_colDefaultValue.col_name = dr["col_name"].ToString();
cds_colDefaultValue.data_type = dr["data_type"].ToString();
cds_colDefaultValue.field1 = l_temp1;
cds_colDefaultValue.field2 = l_temp2;
}
}
}
if(!isexists)
{
cds_col.Append();
cds_col.col_name = dr["col_name"].ToString();
cds_col.field1 = "F";
cds_col.field2 = "T";
}
}
foreach(DataRow dr in l_dtsourceCol.Rows)
{
bool isexists = false;
foreach(DataRow sdr in l_dtaimCol.Rows)
{
if(dr["col_name"].ToString().SameText(sdr["col_name"].ToString()))
{
isexists = true;
}
}
if(!isexists)
{
cds_col.Append();
cds_col.col_name = dr["col_name"].ToString();
cds_col.field1 = "T";
cds_col.field2 = "F";
}
}
}
}
#endregion
(项目源代码已上传云盘)