• c# 自定义分表


    背景
    sqlserver 单表数据过多,数据量已经过亿,查询缓慢,之前是采取手动分表的处理,我是半路接手,现在想对其进行改造,但是项目比较老,无法使用现成的分表插件,我们分表是根据项目来,这里记录下操作过程以及具体代码,希望能给小伙伴提供一些思路

     

    1. //这里在配置文件中设置分表的阈值
    2. <add key="SubmeterThreshold" value="1000000"/>
    1. ///
    2. /// 根据原表名创建新表
    3. ///
    4. ///
    5. ///
    6. public static string CreateSubmeterTable(string sourceTableName)
    7. {
    8. string newTableName = "";
    9. try
    10. {
    11. string DateStr = DateTime.Now.ToString("yyyyMMddHHmmss");
    12. string connectionString =Settings.DBHerperSqlConnection; // 替换为你的数据库连接字符串
    13. newTableName = sourceTableName+"_" + DateStr; // 替换为你的新表名称
    14. using (SqlConnection connection = new SqlConnection(connectionString))
    15. {
    16. connection.Open();
    17. // 复制表结构
    18. string createTableQuery = "SELECT TOP 0 * INTO "+newTableName+" FROM "+sourceTableName+" ";
    19. using (SqlCommand command = new SqlCommand(createTableQuery, connection))
    20. {
    21. command.ExecuteNonQuery();
    22. }
    23. // 复制主键,这里通过语句只能获取到主键名称,和主键字段不一致,所以这里自己写死,且主键名是数据库唯一,所以自行定义
    24. string pkkey = "PK_" + newTableName;
    25. string pkvalue = "主键字段id"
    26. string alterTableQuery = "ALTER TABLE " + newTableName + " ADD CONSTRAINT " + pkkey + " PRIMARY KEY CLUSTERED (" + pkvalue + ")";
    27. using (SqlCommand alterTableCommand = new SqlCommand(alterTableQuery, connection))
    28. {
    29. alterTableCommand.ExecuteNonQuery();
    30. }
    31. // 复制索引
    32. string indexQuery = "SELECT name, type_desc, is_unique, is_primary_key,index_id FROM sys.indexes WHERE OBJECT_NAME(object_id) = '" + sourceTableName + "'";
    33. List modelindex = new List();
    34. using (SqlCommand command = new SqlCommand(indexQuery, connection))
    35. {
    36. using (SqlDataReader reader = command.ExecuteReader())
    37. {
    38. while (reader.Read()) {
    39. bool isPrimaryKey = reader.GetBoolean(3);
    40. if (!isPrimaryKey)
    41. {
    42. ModelIndex entity = new ModelIndex();
    43. entity.indexName = reader.GetString(0) + "_" + DateStr;
    44. entity.indexType = reader.GetString(1);
    45. entity.isUnique = reader.GetBoolean(2);
    46. entity.index_id = reader.GetInt32(4);
    47. modelindex.Add(entity);
    48. }
    49. }
    50. }
    51. }
    52. //复制索引字段
    53. foreach (var item in modelindex)
    54. {
    55. string createIndexQuery = "CREATE " + (item.isUnique ? "UNIQUE " : "") + item.indexType + " INDEX " + item.indexName + " ON " + newTableName + " (";
    56. // 获取索引关联的字段
    57. string indexColumnsQuery = "SELECT name FROM sys.index_columns ic JOIN sys.columns c ON ic.column_id = c.column_id AND ic.object_id = c.object_id WHERE ic.object_id = OBJECT_ID('" + sourceTableName + "') AND ic.index_id = " + item.index_id;
    58. using (SqlCommand indexColumnsCommand = new SqlCommand(indexColumnsQuery, connection))
    59. {
    60. using (SqlDataReader indexColumnsReader = indexColumnsCommand.ExecuteReader())
    61. {
    62. bool firstColumn = true;
    63. while (indexColumnsReader.Read())
    64. {
    65. if (!firstColumn)
    66. {
    67. createIndexQuery += ", ";
    68. }
    69. createIndexQuery += indexColumnsReader.GetString(0);
    70. firstColumn = false;
    71. }
    72. }
    73. }
    74. createIndexQuery += ")";
    75. using (SqlCommand createIndexCommand = new SqlCommand(createIndexQuery, connection))
    76. {
    77. createIndexCommand.ExecuteNonQuery();
    78. }
    79. }
    80. }
    81. }
    82. catch (Exception ex)
    83. {
    84. LogHelper.WriteLog("分表失败:" + ex.Message);
    85. }
    86. return newTableName;
    87. }

    思路提供:
    每次创建项目,都获取下最新分表的数据总量,如果记录数超过设置的阈值,则进行分表处理,且将分表的表名,项目id进行记录
    目的是下次创建项目的时候,根据项目id以及存储的分表表名,查询新表数据是否超出阈值

    手动分表的缺点就是需要重写插入、查询方法,因为没有引用orm,这里 插入数据需要进行改造
    //部分代码如下
     

    1. db.CommandTimeout = 600000;
    2. // 打开数据库连接
    3. db.Connection.Open();
    4. //开启事务
    5. var transaction = db.Connection.BeginTransaction();
    6. db.Transaction = transaction;
    7. try
    8. {
    9. //获取字段属性
    10. var tabledata = SubmeterHerper.GetEntityData(存储数据的实体, true, 1);
    11. string answerSql = string.Format(@"INSERT INTO {0} ({1}) OUTPUT INSERTED.answerid VALUES ({2})", "分表表名", tabledata.Item1, tabledata.Item2);
    12. var result = db.ExecuteQuery<int>(answerSql).FirstOrDefault();
    13. db.Transaction.Commit();
    14. }
    15. catch (Exception ex)
    16. {
    17. // 发生异常时回滚事务
    18. db.Transaction.Rollback();
    19. throw;
    20. }
    21. finally
    22. {
    23. // 关闭数据库连接
    24. db.Connection.Close();
    25. }
    26. ///
    27. /// 获取字段属性
    28. ///
    29. ///
    30. ///
    31. /// 是否存在关联表
    32. /// 如果存在关联,末尾跳过的数量
    33. ///
    34. public static Tuple<string, string> GetEntityData<T>(T entity, bool association = false, int takenum = 1)
    35. {
    36. // 用于拼接字段名
    37. StringBuilder fieldNameBuilder = new StringBuilder();
    38. // 用于拼接字段值
    39. StringBuilder fieldValueBuilder = new StringBuilder();
    40. // 获取entity的类型
    41. Type entityType = entity.GetType();
    42. // 获取entity的所有属性
    43. PropertyInfo[] properties = entityType.GetProperties();
    44. //判断是否存在关联表,如果存在需要跳过关联字段
    45. if (association)
    46. {
    47. properties = properties.Skip(1).Take(properties.Length - (1 + takenum)).ToArray();
    48. }
    49. else
    50. {
    51. properties = properties.Skip(1).ToArray();
    52. }
    53. // 遍历属性
    54. foreach (PropertyInfo property in properties)
    55. {
    56. //主键自增无法通过反射获取,这里去除第一个字段(--默认第一个字段为主键)
    57. // 获取属性名
    58. string fieldName = property.Name;
    59. // 获取属性值
    60. object fieldValue = property.GetValue(entity, null);
    61. // 将属性名拼接到字段名的字符串中
    62. fieldNameBuilder.Append("[").Append(fieldName).Append("],");
    63. // 将属性值拼接到字段值的字符串中
    64. fieldValueBuilder.Append("'").Append(fieldValue).Append("',");
    65. }
    66. // 去除最后一个逗号
    67. fieldNameBuilder.Length--;
    68. fieldValueBuilder.Length--;
    69. // 返回拼接的结果
    70. string fieldNameString = fieldNameBuilder.ToString();
    71. string fieldValueString = fieldValueBuilder.ToString();
    72. return new Tuple<string, string>(fieldNameString, fieldValueString);
    73. }
    74. ///
    75. /// 获取字段属性--被关联专用
    76. ///
    77. ///
    78. ///
    79. /// 被关联字段
    80. /// 需赋的值
    81. ///
    82. public static Tuple<string, string> GetEntityDataGL<T>(T entity,string colname,int value)
    83. {
    84. // 用于拼接字段名
    85. StringBuilder fieldNameBuilder = new StringBuilder();
    86. // 用于拼接字段值
    87. StringBuilder fieldValueBuilder = new StringBuilder();
    88. // 获取entity的类型
    89. Type entityType = entity.GetType();
    90. // 获取entity的所有属性
    91. PropertyInfo[] properties = entityType.GetProperties();
    92. //这因为是关联表,所以去掉首尾,如果存在多个关联,则下面应该是properties.Length -(1+n)
    93. properties = properties.Skip(1).Take(properties.Length - (2)).ToArray();
    94. // 遍历属性
    95. foreach (PropertyInfo property in properties)
    96. {
    97. //主键自增无法通过反射获取,这里去除第一个字段(--默认第一个字段为主键)
    98. // 获取属性名
    99. string fieldName = property.Name;
    100. // 获取属性值
    101. object fieldValue = property.GetValue(entity, null);
    102. if (fieldName==colname)
    103. {
    104. fieldValue = value;
    105. }
    106. // 将属性名拼接到字段名的字符串中
    107. fieldNameBuilder.Append("[").Append(fieldName).Append("],");
    108. // 将属性值拼接到字段值的字符串中
    109. fieldValueBuilder.Append("'").Append(fieldValue).Append("',");
    110. }
    111. // 去除最后一个逗号
    112. fieldNameBuilder.Length--;
    113. fieldValueBuilder.Length--;
    114. // 返回拼接的结果
    115. string fieldNameString = fieldNameBuilder.ToString();
    116. string fieldValueString = fieldValueBuilder.ToString();
    117. return new Tuple<string, string>(fieldNameString, fieldValueString);
    118. }
    119. }

  • 相关阅读:
    操作系统学习知识点(1)
    docker容器安装mysql
    基于开源ERDDAP的海洋学科数据分发技术简介
    亚马逊云科技数据分析为这伴科技赋能,实现“零”中断目标
    【算法系列】非线性最小二乘求解-梯度下降法
    Go-Excelize API源码阅读(十九)——SetHeaderFooter
    集团资金管理BI分析的三个关键节点
    GameFi 行业下滑但未出局| June Report
    一、python Django基础配置[基础内容]
    ADS算力芯片的多模型架构研究
  • 原文地址:https://blog.csdn.net/huxinyu0208/article/details/134076265