博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据库扩展表设计过程记录
阅读量:5371 次
发布时间:2019-06-15

本文共 6745 字,大约阅读时间需要 22 分钟。

  这两天需要实现一个动态表单设计,面对着属性的不确定,要能够容纳不同的属性进来,之前也接触过这方面的设计,但是没有设计好,导致问题太多,这一次参考一些前辈们的经验后,再次尝试一番,通过动态设计表结构,以达到任务要求。

 

一、常用动态表结构设计方式

  1、动态修改表,适应变化。

  2、预留字段实现动态表结构(伪动态)。

  3、将动态属性全部保存在一个字段中,xml或是json格式保存(版本号+通用列)。

  4、表结构和表数据分离,xml形式分别保存表结构和表数据。

  5、横向表转纵向表(属性字段行存储)。

  对于这几种方式,或许不同的选用适用不同的形式,我选择了最后一种来实现我现有的设计,这种方式个人感觉更加灵活,可以更方便的扩展属性(适合的才是最好的)。

 

二、横向表转纵向表初步设计

  首先看下横向表的设计,如果采用横向表,因为业务的需要,要容纳好几种行业的信息进来,这样一来整张表的字段数将会非常多,从设计或是维护角度来讲,这都是一个棘手的芋头,因此传统的横向表设计不能满足现有的需求了。

  

  按照横向表转纵向表的思路对表结构进行更改,通过设置成键值对形式,得到如下表结构。

  

   在这里可能有一个情况得想清楚了,我们每一次增加一条记录的时候,记录内的信息是作为同一批添加进来的,反过来,当我从数据库中取出数据时,也应该需要把同一批的记录信息取出来,因此在上面的设计中再加入一个GroupId用来区分同一批次的数据,而至于属性的重复量很大,之后将进行优化处理。

   

  现在看到这个结构时,对于动态扩充属性来讲,已经是达到了我的预期了,对于数据库设计时,将检测指标及限值均设置成字符串的,分组号我采用时间戳的形式进行存储,当然也可以采用其它更为稳妥的方式,如Guid或是自定义ID等。

  对于好多检测指标名称出现重复情况,我将这部分单独抽出来一张表用于存储检测指标属性,需要注意的是此处的名称需要在某个检测项目编号下唯一,该部分属性先在界面上呈现,其次呈现对应的数据,如果某列增加或删除了,对应展示行也就空着了一个数据或是消失了一个数据单元,而对于改了指标名称或是对外的展示名称,都不会影响数据的存储,通过默认值可以使得有些常用值不要再二次输入,减少工作量。

   

  在具体检测限值中完成对检测指标的关联,加入一列完成外键关联,同时对原有表内存在的列可以进行优化,因为这些信息都在检测指标中存在了,不必要的数据冗余还是不存在为好。单从现在的表结构来看,当我们按照在增加一些额外的属性时,可以做到不要去修改表结构,而只需要对表内数据进行管理即可。

   

 

三、代码实现过程

  此处我采用Asp.Net Core MVC并利用Razor语法,更为方便的完成表单展示工作,当然对于这部分工作,采用js或模板等等都是可以快速完成的。

  1、首先对于界面添加检测指标的设计,遵循普通的表单设计方式即可,此处增加了两个隐藏元素,为适用于编辑场景而存在,此处快速略过提交到后台并保存到数据库的过程,可能需要在后台验证提交的名称的唯一性。

   2、对于增加具体的检测记录,需要先读取到整个检测项目下的所有检测指标,然后实现生成表单的过程,按照如下的思路一步一步实现:

   

  对于第一步,从数据库获取指定检测项目的检测指标,该步可以直接利用提供的id做一次查询即可得到相应的指标集合。然后在前端循环输出时,利用Razor语法完成动态渲染Html,生成label和input元素,依照之前设计检测指标时的name唯一,可以在此处设计表单时指定name属性。

@foreach (var item in Model) {
}

   接下来可以完成表单的输入工作了,并提交到后台完成保存到数据库中,如我此处,新增记录时,保存到数据库前,先生成分组号,以此来区分这些指标下的数据是一个批次的,然后完成保存到数据库的过程。

public async Task ConvertTableToEvaluationLimitValues(TestItemCode_EvaluationStandardSubItem assignTestItemCode, Dictionary
evaluationLimitValues){ var groupId = DateTimeHelper.GetTimeStamp(); var evaluationIndexes = assignTestItemCode.EvaluationStandardSubItem.EvaluationStandard.EvaluationIndexes.ToList(); foreach (var item in evaluationLimitValues) { var evaluationIndex = evaluationIndexes.Where(e => e.Name == item.Key).FirstOrDefault(); if (evaluationIndex == null) continue; var evaluationLimitValue = new EvaluationLimitValue(evaluationIndex.Id, assignTestItemCode.Id, item.Value, groupId); await _evaluationLimitValueRepository.InsertAsync(evaluationLimitValue); }}

   对于这部分的设计,做一点更改也适用于更新操作,但是得注意到,更新表单时,表单上展示的检测指标可能存在增加或是删除的情形,因此对于存在的记录我们可以展示出来,不存在的则留空,当提交到数据库时,需要做一次比对过程,对那部分增加的检测指标需要保存到数据库中,当然对于已有的检测指标也存在变更的可能,因此需要做一次判断,当有变更时更新,没有时不处理。

public async Task UpdateEvaluationLimitValues(TestItemCode_EvaluationStandardSubItem assignTestItemCode, string groupId, Dictionary
evaluationLimitValues){ //获取当前的检测指标 var evaluationIndexes = assignTestItemCode.EvaluationStandardSubItem.EvaluationStandard.EvaluationIndexes.ToList(); //目标分组已存在的检测限值 var results = await _evaluationLimitValueRepository.GetAll() .Where(e => e.TestItemCode_EvaluationStandardSubItemId == assignTestItemCode.Id && e.GroupId == groupId) .Include(e => e.EvaluationIndex).ToListAsync(); //已存在的检测限值对应于检测指标名称列表 var existedEvaluationLimitValueNameList = results.Select(r => r.EvaluationIndex.Name).ToList(); //需新增的检测限值记录 var addEvaluationLimitValueList = evaluationLimitValues.Keys.Except(existedEvaluationLimitValueNameList).ToList(); foreach (var key in addEvaluationLimitValueList) { var evaluationIndexId = evaluationIndexes.Where(e => e.Name == key).FirstOrDefault().Id; var evaluationLimitValue = new EvaluationLimitValue(evaluationIndexId, assignTestItemCode.Id, evaluationLimitValues[key], groupId); await _evaluationLimitValueRepository.InsertAsync(evaluationLimitValue); //移除记录 evaluationLimitValues.Remove(key); } //更新已有检测限值记录值 foreach (var key in evaluationLimitValues.Keys) { var editEvaluationLimitValues = results.Where(r => r.EvaluationIndex.Name == key && r.LimitValue != evaluationLimitValues[key]).FirstOrDefault(); if (editEvaluationLimitValues != null) { editEvaluationLimitValues.LimitValue = evaluationLimitValues[key]; await _evaluationLimitValueRepository.UpdateAsync(editEvaluationLimitValues); } }}

   3、完成检测记录的表格展示,此处需要遵循一个原则,就是先展示检测指标,也就是先展示属性列,其次展示数据值,只有相应的属性列存在,展示的数据值才有意义,通过指定的编号Id获取相应的属性集合并展示在前端,利用Razor语法循环输出th元素,来产生表格行头。

@foreach (var item in Model) {
}
@Html.Raw(item.DisplayName)

   表格展示完毕时便是数据开始呈现的时机,通过获取检测限值中的记录,注意这里的记录会有多条存在的,我们需要将纵向表结构转换成横向的json格式,用于前端读取,注意这里得把分组号加入进来,这是属于同一批次的标识。

  

  通过纵向转横向,可以得到字典类型的list集合,然后再返回前序列化成json格式,便是前端需要的格式。

public async Task
>> ConvertEvaluationLimitValuesToTable(Guid assignedTestItemCodeId){ //分组后的评价限值 var results = await _evaluationLimitValueRepository.GetAll() .Where(e => e.TestItemCode_EvaluationStandardSubItemId == assignedTestItemCodeId) .Include(e => e.EvaluationIndex) .GroupBy(e => e.GroupId).ToListAsync(); List
> convertResultList = new List
>(); foreach (var result in results) { Dictionary
tempResultList = new Dictionary
{ { EvaluationLimitValue.GetGroupIdName(), result.ElementAt(0).GroupId }//增加分组号GroupId }; foreach (var item in result) { tempResultList.Add(item.EvaluationIndex.Name, item.LimitValue); } convertResultList.Add(tempResultList); } return convertResultList;}

 

四、设计实现效果

  1、实现动态增加属性列,尽管没有太丰富的功能,但是已经满足我现有的需求了,或许还能依据此得到更复杂的表单属性列设计。

  

  2、增加表单具体值,依据增加的属性列完成相应值填写。

  

  3、再次增加属性列后增加表单具体值,实现属性列的增加删除和修改后,仍然可以适用而无需手动修改表结构。

  

  至此,动态表单的简单设计工作已经完成,过程较为简单,没有融入更多的比如多选,单选、数值型的设计,纯字符类型设计工作。

 

2019-05-27,望技术有成后能回来看见自己的脚步

转载于:https://www.cnblogs.com/CKExp/p/10919740.html

你可能感兴趣的文章
Java 将指定字符串连接到此字符串的结尾 concat()
查看>>
Hibernate Criterion
查看>>
Python知识
查看>>
我们为什么要搞长沙.NET技术社区(三)
查看>>
杭电acm Cake
查看>>
js函数中this的指向
查看>>
c++ 引用方式传递数组
查看>>
HBase学习之路 (九)HBase phoenix的使用
查看>>
LeetCode() Remove Duplicates from Sorted Array II
查看>>
【svn】idea svn 文件上会出现一个破书
查看>>
cocos2d-x 3.0 场景切换特效汇总(转)
查看>>
The SortedMap Interface
查看>>
SniperOJ-leak-x86-64
查看>>
bzoj 4260: Codechef REBXOR (01 Trie)
查看>>
学好python
查看>>
css-IE中的border-radius和box-shadow
查看>>
利用bootstrap和webform的异步CRUD及分页
查看>>
Saiku资源帖
查看>>
解决手机页面中点击文本框,网页放大问题
查看>>
2-5
查看>>