自研ORM Include拆分查询(递归算法 支持无限层级) 性能优化探讨
最近我在优化 Include 拆分查询,贴出源码供大家交流探讨是否还有优化空间。
测试代码
1 Console.WriteLine($"总记录数:{db.Query<Category>().Count()}");
2
3 var stopwatch1 = new Stopwatch();
4 stopwatch1.Start();
5 var data1 = db.Query<Category>().Include(i => i.Products).ToList();
6 stopwatch1.Stop();
7
8 Console.WriteLine($"Include查询 耗时:{stopwatch1.ElapsedMilliseconds} ms {stopwatch1.ElapsedMilliseconds / 1000.00}s");
9
10 //Console.WriteLine(Json.Serialize(data1[0].Products[0]));
11
12 var stopwatch2 = new Stopwatch();
13 stopwatch2.Start();
14 var data2 = db.Query<Category>().ToList();
15
16 foreach (var item in data2)
17 {
18 item.Products = db.Query<Product>().Where(w => w.CategoryId == item.CategoryId).ToList();
19 }
20 stopwatch2.Stop();
21
22 Console.WriteLine($"循环查询 耗时:{stopwatch2.ElapsedMilliseconds} ms {stopwatch2.ElapsedMilliseconds / 1000.00}s");
测试结果
Include 生成的Sql语句
SELECT `CategoryId`,`CategoryName` FROM `Category`
--------------------------
--------------------------
SELECT a.`CategoryId`,a.`CategoryName`,b.`ProductId`,b.`CategoryId`,b.`ProductCode`,b.`ProductName`,b.`DeleteMark`,b.`CreateTime`,b.`Custom1`,b.`Custom2`,b.`Custom3`,b.`Custom4`,b.`Custom5`,b.`Custom6`,b.`Custom7`,b.`Custom8`,b.`Custom9`,b.`Custom10`,b.`Custom11`,b.`Custom12` FROM `Category` `a`
INNER JOIN `Product` `b` ON `a`.`CategoryId` = `b`.`CategoryId`
ORDER BY `b`.`ProductId`
Include 方法实现
1 /// <summary>
2 /// 包括
3 /// </summary>
4 /// <typeparam name="TProperty"></typeparam>
5 /// <param name="expression">表达式</param>
6 /// <returns></returns>
7 public IInclude<T, TProperty> Include<TProperty>(Expression<Func<T, TProperty>> expression) where TProperty : class
8 {
9 var result = expression.ResolveSql(new ResolveSqlOptions()
10 {
11 DbType = ado.DbOptions.DbType,
12 ResolveSqlType = ResolveSqlType.NewColumn,
13 IgnoreParameter = true,
14 IgnoreIdentifier = true
15 });
16
17 var propertyType = typeof(TProperty);
18
19 if (QueryBuilder.IncludeInfos.Any(a => a.PropertyType.FullName == propertyType.FullName))
20 {
21 throw new Exception($"属性名称:{result.SqlString} 不能重复使用Include方法.");
22 }
23
24 var type = propertyType;
25
26 if (type.IsArray)
27 {
28 type = type.GetElementType();
29 }
30 else if (type.IsGenericType)
31 {
32 type = type.GenericTypeArguments[0];
33 }
34
35 var queryBuilder = SqlBuilderFactory.CreateQueryBuilder(ado.DbOptions.DbType);
36 queryBuilder.EntityDbMapping = typeof(T).GetEntityDbMapping();
37 queryBuilder.EntityDbMapping.Alias = "a";
38
39 var includeInfo = new IncludeInfo();
40 includeInfo.EntityDbMapping = type.GetEntityDbMapping();
41 includeInfo.EntityDbMapping.Alias = "b";
42
43 includeInfo.PropertyName = result.SqlString;
44 includeInfo.PropertyType = propertyType;
45 includeInfo.Type = type;
46 includeInfo.QueryBuilder = queryBuilder;
47
48 QueryBuilder.IncludeInfos.Add(includeInfo);
49
50 return new IncludeProvider<T, TProperty>(ado, QueryBuilder, includeInfo);
51 }
IncludeInfo 实体结构
1 using Fast.Framework.Abstract;
2 using Fast.Framework.Interfaces;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Text;
7 using System.Threading.Tasks;
8
9 namespace Fast.Framework.Models
10 {
11
12 /// <summary>
13 /// 包括信息
14 /// </summary>
15 public class IncludeInfo
16 {
17 /// <summary>
18 /// 属性类型
19 /// </summary>
20 public Type PropertyType { get; set; }
21
22 /// <summary>
23 /// 类型
24 /// </summary>
25 public Type Type { get; set; }
26
27 /// <summary>
28 /// 属性名称
29 /// </summary>
30 public string PropertyName { get; set; }
31
32 /// <summary>
33 /// 实体数据库映射
34 /// </summary>
35 public EntityDbMapping EntityDbMapping { get; set; }
36
37 /// <summary>
38 /// 条件列
39 /// </summary>
40 public string WhereColumn { get; set; }
41
42 /// <summary>
43 /// 查询建造
44 /// </summary>
45 public QueryBuilder QueryBuilder { get; set; }
46
47 }
48 }
数据绑定核心类
1 using System;
2 using System.Reflection;
3 using System.Collections;
4 using System.Collections.Generic;
5 using System.Data.Common;
6 using System.Data;
7 using System.Linq;
8 using System.Text;
9 using System.Threading.Tasks;
10 using Fast.Framework.Abstract;
11 using Fast.Framework.Interfaces;
12 using Fast.Framework.Models;
13
14 namespace Fast.Framework.Extensions
15 {
16
17 /// <summary>
18 /// 查询建造扩展类
19 /// </summary>
20 public static class QueryBuilderExtensions
21 {
22
23 private static readonly MethodInfo fristBuildMethod;
24
25 private static readonly MethodInfo listBuildMethod;
26
27 private static readonly MethodInfo ofTypeMethod;
28
29 private static readonly MethodInfo ofObjTypeMethod;
30 private static readonly MethodInfo ofObjTypeGenericMethod;
31
32 private static readonly MethodInfo toArrayMethod;
33
34 private static readonly MethodInfo toListMethod;
35
36 private static readonly MethodInfo toObjListMethod;
37 private static readonly MethodInfo toObjListGenericMethod;
38
39 /// <summary>
40 /// 构造方法
41 /// </summary>
42 static QueryBuilderExtensions()
43 {
44 fristBuildMethod = typeof(DbDataReaderExtensions).GetMethod("FristBuild", new Type[] { typeof(DbDataReader) });
45
46 listBuildMethod = typeof(DbDataReaderExtensions).GetMethod("ListBuild", new Type[] { typeof(DbDataReader) });
47
48 ofTypeMethod = typeof(Enumerable).GetMethod("OfType");
49
50 ofObjTypeMethod = typeof(Enumerable).GetMethod("OfType");
51 ofObjTypeGenericMethod = ofObjTypeMethod.MakeGenericMethod(typeof(object));
52
53 toArrayMethod = typeof(Enumerable).GetMethod("ToArray");
54
55 toListMethod = typeof(Enumerable).GetMethod("ToList");
56
57 toObjListMethod = typeof(Enumerable).GetMethod("ToList");
58 toObjListGenericMethod = toObjListMethod.MakeGenericMethod(typeof(object));
59 }
60
61 /// <summary>
62 /// 初始化
63 /// </summary>
64 /// <param name="dbType">数据库类型</param>
65 /// <param name="includeInfo">包括信息</param>
66 /// <param name="isList">是否列表</param>
67 private static void Init(Models.DbType dbType, IncludeInfo includeInfo, bool isList)
68 {
69 var identifier = dbType.MappingIdentifier();
70 var parameterSymbol = dbType.MappingParameterSymbol();
71
72 //条件列
73 if (string.IsNullOrWhiteSpace(includeInfo.WhereColumn))
74 {
75 var whereColumn = includeInfo.QueryBuilder.EntityDbMapping.ColumnsInfos.FirstOrDefault(f => f.IsPrimaryKey || f.ColumnName.ToUpper().EndsWith("ID"));
76 includeInfo.WhereColumn = whereColumn.ColumnName;
77 }
78
79 //排序列
80 var orderByColumn = includeInfo.EntityDbMapping.ColumnsInfos.FirstOrDefault(f => f.IsPrimaryKey || f.ColumnName.ToUpper().EndsWith("ID"));
81 if (orderByColumn != null)
82 {
83 if (includeInfo.QueryBuilder.OrderBy.Count == 0)
84 {
85 includeInfo.QueryBuilder.OrderBy.Add($"{identifier.Insert(1, includeInfo.EntityDbMapping.Alias)}.{identifier.Insert(1, orderByColumn.ColumnName)}");
86 }
87 }
88
89 if (!isList)
90 {
91 includeInfo.QueryBuilder.Where.Add($"{identifier.Insert(1, includeInfo.EntityDbMapping.Alias)}.{identifier.Insert(1, includeInfo.WhereColumn)} = {parameterSymbol}{includeInfo.WhereColumn}");
92 }
93
94 var joinInfo = new JoinInfo();
95 joinInfo.IsInclude = true;
96 joinInfo.JoinType = JoinType.Inner;
97 joinInfo.EntityDbMapping = includeInfo.EntityDbMapping;
98 joinInfo.Where = $"{identifier.Insert(1, includeInfo.QueryBuilder.EntityDbMapping.Alias)}.{identifier.Insert(1, includeInfo.WhereColumn)} = {identifier.Insert(1, includeInfo.EntityDbMapping.Alias)}.{identifier.Insert(1, includeInfo.WhereColumn)}";
99
100 includeInfo.QueryBuilder.Join.Add(joinInfo);
101 }
102
103 /// <summary>
104 /// Include数据绑定
105 /// </summary>
106 /// /// <param name="queryBuilder">查询建造</param>
107 /// <param name="ado">Ado</param>
108 /// <param name="obj">对象</param>
109 /// <returns></returns>
110 public static void IncludeDataBind(this QueryBuilder queryBuilder, IAdo ado, object obj)
111 {
112 if (queryBuilder.IncludeInfos.Count > 0 && obj != null)
113 {
114 var type = obj.GetType();
115
116 var isMultipleResult = false;
117
118 if (type.IsArray)
119 {
120 isMultipleResult = true;
121 type = type.GetElementType();
122 }
123 else if (type.IsGenericType)
124 {
125 isMultipleResult = true;
126 type = type.GenericTypeArguments[0];
127 }
128
129 foreach (var includeInfo in queryBuilder.IncludeInfos)
130 {
131 Init(ado.DbOptions.DbType, includeInfo, isMultipleResult);
132
133 var propertyInfo = type.GetProperty(includeInfo.PropertyName);
134
135 object data = null;
136
137 if (!isMultipleResult)
138 {
139 var parameterValue = type.GetProperty(includeInfo.WhereColumn).GetValue(obj);
140 includeInfo.QueryBuilder.DbParameters.Add(new DbParameterEx(includeInfo.WhereColumn, parameterValue));
141 }
142
143 var sql = includeInfo.QueryBuilder.ToSqlString();
144 var reader = ado.ExecuteReader(CommandType.Text, sql, ado.CreateParameter(includeInfo.QueryBuilder.DbParameters));
145
146 var fristBuildGenericMethod = fristBuildMethod.MakeGenericMethod(includeInfo.Type);
147
148 var listBuildGenericMethod = listBuildMethod.MakeGenericMethod(includeInfo.Type);
149
150 var ofTypeGenericMethod = ofTypeMethod.MakeGenericMethod(includeInfo.Type);
151
152 var toArrayGenericMethod = toArrayMethod.MakeGenericMethod(includeInfo.Type);
153
154 var toListGenericMethod = toListMethod.MakeGenericMethod(includeInfo.Type);
155
156 if (isMultipleResult)
157 {
158 data = listBuildGenericMethod.Invoke(null, new object[] { reader });
159
160 data = ofObjTypeGenericMethod.Invoke(null, new object[] { data });
161
162 data = toObjListGenericMethod.Invoke(null, new object[] { data });
163
164 var list = data as List<object>;
165
166 if (list.Any())
167 {
168 var whereColumnProInfo = list.FirstOrDefault()?.GetType().GetProperty(includeInfo.WhereColumn);
169 if (whereColumnProInfo != null)
170 {
171 foreach (var item in obj as IList)
172 {
173 var parameterValue = type.GetProperty(includeInfo.WhereColumn).GetValue(item);
174
175 object value = null;
176
177 if (includeInfo.PropertyType.IsArray || includeInfo.PropertyType.IsGenericType)
178 {
179 value = list.Where(w => Convert.ToString(whereColumnProInfo.GetValue(w)) == Convert.ToString(parameterValue));
180
181 value = ofTypeGenericMethod.Invoke(null, new object[] { value });
182
183 if (includeInfo.PropertyType.IsArray)
184 {
185 value = toArrayGenericMethod.Invoke(null, new object[] { value });
186 }
187 else if (includeInfo.PropertyType.IsGenericType)
188 {
189 value = toListGenericMethod.Invoke(null, new object[] { value });
190 }
191 }
192 else
193 {
194 value = list.FirstOrDefault(w => Convert.ToString(whereColumnProInfo.GetValue(w)) == Convert.ToString(parameterValue)).ChanageType(includeInfo.PropertyType);
195 }
196
197 propertyInfo.SetValue(item, value);
198 }
199 }
200 }
201 }
202 else
203 {
204 if (includeInfo.PropertyType.IsArray || includeInfo.PropertyType.IsGenericType)
205 {
206 data = listBuildGenericMethod.Invoke(null, new object[] { reader });
207
208 if (includeInfo.PropertyType.IsArray)
209 {
210 data = toArrayGenericMethod.Invoke(null, new object[] { data });
211 }
212 }
213 else
214 {
215 data = fristBuildGenericMethod.Invoke(null, new object[] { reader });
216 }
217 propertyInfo.SetValue(obj, data);
218 }
219
220 if (includeInfo.QueryBuilder.IncludeInfos.Count > 0)
221 {
222 includeInfo.QueryBuilder.IncludeDataBind(ado, data);
223 }
224
225 }
226 }
227 }
228
229 }
230 }
翻译
搜索
复制
自研ORM Include拆分查询(递归算法 支持无限层级) 性能优化探讨的更多相关文章
- MySQL查询语句执行过程及性能优化(JOIN/ORDER BY)-图
http://blog.csdn.net/iefreer/article/details/12622097 MySQL查询语句执行过程及性能优化-查询过程及优化方法(JOIN/ORDER BY) 标签 ...
- MySQL查询语句执行过程及性能优化-查询过程及优化方法(JOIN/ORDER BY)
在上一篇文章MySQL查询语句执行过程及性能优化-基本概念和EXPLAIN语句简介中介绍了EXPLAIN语句,并举了一个慢查询例子:
- WebAPI增加Area以支持无限层级同名Controller
原文:WebAPI增加Area以支持无限层级同名Controller 微软的WebAPI默认实现逻辑 默认实现中不支持同名Controller,否则在访问时会报HttpError,在网上找到了各种路由 ...
- MySQL查询语句执行过程及性能优化-基本概念和EXPLAIN语句简介
网站或服务的性能关键点很大程度在于数据库的设计(假设你选择了合适的语言开发框架)以及如何查询数据上. 我们知道MySQL的性能优化方法,一般有建立索引.规避复杂联合查询.设置冗余字段.建立中间表.查询 ...
- IN、EXISTS的相关子查询用INNER JOIN 代替--性能优化
如果保证子查询没有重复 ,IN.EXISTS的相关子查询可以用INNER JOIN 代替.比如: IN.EXISTS的相关子查询用INNER JOIN 代替--sql2000性能优化
- Java编程:将具有父子关系的数据库表数据转换为树形结构,支持无限层级
在平时的开发工作中,经常遇到这样一个场景,在数据库中存储了具有父子关系的数据,需要将这些数据以树形结构的形式在界面上进行展示.本文的目的是提供了一个通用的编程模型,解决将具有父子关系的数据转换成树形结 ...
- SQl语句查询性能优化
[摘要]本文从DBMS的查询优化器对SQL查询语句进行性能优化的角度出发,结合数据库理论,从查询表达式及其多种查询条件组合对数据库查询性能优化进行分析,总结出多种提高数据库查询性能优化策略,介绍索引的 ...
- Unity技术支持团队性能优化经验分享
https://mp.weixin.qq.com/s?__biz=MzU5MjQ1NTEwOA==&mid=2247490321&idx=1&sn=f9f34407ee5c5d ...
- python 之 Django框架(orm单表查询、orm多表查询、聚合查询、分组查询、F查询、 Q查询、事务、Django ORM执行原生SQL)
12.329 orm单表查询 import os if __name__ == '__main__': # 指定当前py脚本需要加载的Django项目配置信息 os.environ.setdefaul ...
- ORM单表查询,跨表查询,分组查询
ORM单表查询,跨表查询,分组查询 单表查询之下划线 models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值models ...
随机推荐
- GitLab私有化部署 - CI/CD - 持续集成/交付/部署 - 源代码托管 & 自动化部署
预期目标 源代码管理 借助GitLab实现源代码托管,私有化部署版本,创建项目,创建用户组,分配权限,项目的签入/牵出等. 自动化部署 源代码产生变更时(如签入),自动化编译并发布到指定服务器中部署, ...
- swoole学习笔记
一.服务端 0. swoole常用的配置项: daemonize = true 守护进程化 worker_num #swoole配置参数 设置启动的Worker进程数: 如 1 个请求耗时 100ms ...
- HTTPS实现原理分析
概述 在上一节中介绍了两种加密方法 对称加密 非对称加密 其中对称加密性能高,但是有泄露密钥的风险,而非对称加密相反,加密性能较差,但是密钥不易泄露,那么能不能把他们进行一下结合呢? HTTPS采用混 ...
- 使用HTML表单收集数据
1.什么是表单 在项目开发过程中,凡是需要用户填写的信息都需要用到表单. #2.form标签 在HTML中我们使用form标签来定义一个表单.而对于form标签来说有两个最重要的属性:action和m ...
- Response对象页面重定向、时间的动态显示
Response对象 response对象主要用于对客户端的请求进行回应,将web服务器处理后的结果发回给客户端,封装了jsp产生的响应,并发送到客户端响应客户端的请求,请求的数据可以是各种数据类型, ...
- Codeforces 1684 E. MEX vs DIFF
题意 给你n个非负整数的数列a,你可以进行K次操作,每次操作可以将任意位置的数数更改成任意一个非负整数,求操作以后,DIFF(a)-MEX(a)的最小值:DIFF代表数组中数的种类.MEX代表数组中未 ...
- java中HashMap的设计精妙在哪?
摘要:本文结合图解和问题,教你一次性搞定HashMap 本文分享自华为云社区<java中HashMap的设计精妙在哪?用图解和几个问题教你一次性搞定HashMap>,作者:breakDaw ...
- 题解 P6355 [COCI2007-2008#3] DEJAVU
kcm的原题.. 貌似是个组合数(? \(\sf {Solution}\) 对于每一个点,我们需要统计与它同一行的点数\(a\) 和同一列的点数\(b\) ,则该点对结果\(ans\) 的贡献为\(( ...
- 一、什么是Kubernetes
一.什么是Kubernetes 它是一个全新的基于容器技术的分布式架构领先方案,确切地说,Kubernetes是谷歌严格保密十几年的秘密武器Borg的一个开源版本.Borg是谷歌内部使用的大规模集群 ...
- Mysql之MGR高可用实战案例
MGR高可用实战案例 1.环境准备 node1 rocky8.6 10.0.0.8 node2 rocky8.6 10.0.0.18 node3 rocky8.6 10.0.0.28 2.所有节点更改 ...