为数据库定长字段添加数据
需求:使用C#做一个工具,把csv中的数据追加到DB中。
描述:表PharmacyMemo中存在一个定长600的字段memo,现在有一个csv文件记录了患者的memo数据,根据患者ID+日期,如果数据库中存在该患者数据,那么就把csv的memo内容添加到既存数据中,如果不存在那么就插入一条新的数据。
已知csv文件中的memo最长不会超过600。
因为觉得还可以优化,先记录下简单实现,后面有时间再看看怎么优化。
// 把文件转为DTO
List<MemoCsvDto> csvDtoList = ConvertCsvFileToDto(memoFile);
// 根据患者号分组
Dictionary<int, List<MemoCsvDto>> benkjnoDic = csvDtoList.GroupBy(s => s.NextKjno)
.ToDictionary(g => g.Key, g => g.ToList());
// 然后根据患者号排序
var sortedCsvDic = (from entry in benkjnoDic
orderby entry.Key
select entry);
// 获得数据库中所有PharmacyMemo数据(因为库中数据不是太多,因此直接查全表,这里可以根据情况改进)
// C# 版本较低时不支持 out int intValue的形式,必须提前声明
int maxKey = 0;
List<PharmacyMemoDto> existPharmacyMemoList = GetAllKanjaPharmacyMemos(out maxKey);
// 根据患者号加日期分组
Dictionary<string, List<PharmacyMemoDto>> existPharmacyMemoDic =
existPharmacyMemoList.GroupBy(s => GetPharmacyMemoGroupKey(s))
.ToDictionary(g => g.Key, g => g.ToList());
// 准备好要插入的数据
List<PharmacyMemoDto> insertList = new List<PharmacyMemoDto>();
List<PharmacyMemoDto> updateList = new List<PharmacyMemoDto>();
// 字符串拼接的stringBuilder
StringBuilder sb = new StringBuilder();
foreach (var kjnoPair in sortedCsvDic)
{
int nextKjno = kjnoPair.Key;
// 判断非法数据,如果患者表中没有这个患者号就不继续
if (!existNextKjnoSet.Contains(nextKjno))
{
// C#版本较低时不支持$""的形式
Log.Info(string.Format("NEXTの患者テーブルにKJNO:{0}はありません。", nextKjno));
continue;
}
List<MemoCsvDto> oneKanjaCsvList = kjnoPair.Value;
// 根据日期分组
// 因为C#的dictionary的键不能为null,因此使用一个自定义类来作为Key
Dictionary<NullableKey<DateTime?>, List<MemoCsvDto>> groupByEditDayDic =
oneKanjaCsvList.GroupBy(s => new NullableKey<DateTime?>(s.EditDay))
.ToDictionary(g => g.Key, g => g.ToList());
foreach (var editDayPair in groupByEditDayDic)
{
sb.Clear();
DateTime? editday = editDayPair.Key.Value;
List<MemoCsvDto> tobeSaveList = editDayPair.Value.OrderBy(s => s.RowNumber).ToList();
string csvGroupKey = GetPharmacyMemoGroupKey(nextKjno, editday);
PharmacyMemoDto dbDto;
if (existPharmacyMemoDic.ContainsKey(csvGroupKey))
{
// 找到DB主键最大的数据
dbDto = existPharmacyMemoDic[csvGroupKey].OrderBy(dto => dto.MemoCode).Reverse().First();
sb.Append(dbDto.MemoText);
}
else
{
// 库中不存在时就虚构一个数据
maxKey++;
dbDto = new PharmacyMemoDto();
dbDto.MemoCode = maxKey;
dbDto.OperatorCode = nextKjno;
dbDto.OperatorFlag = 1;
dbDto.MemoText = string.Empty;
dbDto.EditDay = editday ?? DEFAULT_EDITDAY;
// 自定义追加的字段,非DB字段
dbDto.IsDbData = false;
}
PharmacyMemoDto currentDto = dbDto;
// 向insertList或者updateList插入的时机就是长度大于600或者最后一个
{
for (int i = 0; i < tobeSaveList.Count; i++)
{
MemoCsvDto csvDto = tobeSaveList[i];
// 长度大于600时
if ((Encoding.Default.GetByteCount(sb.ToString())
+ Encoding.Default.GetByteCount(csvDto.Memo)
+ Encoding.Default.GetByteCount(Environment.NewLine)) > 600)
{
// 保存当前dto
currentDto.MemoText = sb.ToString();
if (currentDto.IsDbData)
{
updateList.Add(currentDto);
}
else
{
insertList.Add(currentDto);
}
sb.Clear();
sb.Append(csvDto.Memo);
// 创建新的dto
maxKey++;
currentDto = new PharmacyMemoDto();
currentDto.MemoCode = maxKey;
currentDto.OperatorCode = nextKjno;
currentDto.OperatorFlag = 1;
currentDto.MemoText = sb.ToString();
currentDto.EditDay = editday ?? DEFAULT_EDITDAY;
currentDto.IsDbData = false;
}
else
{
// 小于600时只需要拼接memo字段即可
if (sb.ToString() == "")
{
sb.Append(csvDto.Memo);
}
else
{
sb.AppendLine().Append(csvDto.Memo);
}
}
if (i == tobeSaveList.Count - 1)
{
currentDto.MemoText = sb.ToString();
if (currentDto.IsDbData)
{
updateList.Add(currentDto);
}
else
{
insertList.Add(currentDto);
}
}
}
}
}
}
/// <summary>
/// use this class to solve dictionary's key can't be null
/// </summary>
/// <typeparam name="T"></typeparam>
class NullableKey<T>
{
private readonly T _value;
public NullableKey(T value)
{
_value = value;
}
public T Value
{
get { return _value; }
}
public override bool Equals(object obj)
{
var key = obj as NullableKey<T>;
return key != null &&
EqualityComparer<T>.Default.Equals(Value, key.Value);
}
public override int GetHashCode()
{
return -1937169414 + EqualityComparer<T>.Default.GetHashCode(Value);
}
}
题外话:
如果再复杂一点,可以根据DFA画出状态变化图,然后在进行编码。