[NewLife.XCode]高级查询(化繁为简、分页提升性能)
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中,代表作有百亿级大数据实时计算项目。
开源地址:https://github.com/NewLifeX/X(求star, 754+)
扩展查询
前文《[NewLife.XCode]实体类详解》中有讲到扩展查询,XCode生成实体类代码时,在模型类有一个region叫“扩展查询”,一般是FindByAbc/FindAllByAbc的形式。
扩展查询以数据表索引为依据来生成:
- 唯一索引(含主键)生成FindByAbc方法(如FindByName),返回单个对象;
- 非唯一索引生成FindAllByAbc方法(如FindAllByClassID),返回对象列表(非null);
如上图,可知Entity实体基类内部,查询方法分为单对象查询的Find和对象列表的查询FindAll。
实际上,Find最终调用FindAll方法查一行。
Find/FindAll有多个重载,最主要的地方都是构造where查询条件。
下划线_是每个实体类都有的内嵌类,它包含了每一个字段的Field引用,借助运算符重载,可以很方便的构造查询条件,例如上面的_.Name == name最终会生成 where Name='Stone'
因为是内嵌类,在实体类内部使用的时候非常方便。但要是想要实体类外部使用,就麻烦很多了,需要带上实体类类名。
原则:XCode是充血模型,不管多么简单的查询,建议都封装Find/FindAll/Search等方法供外部使用。
高级表达式查询
仅靠一两个字段的简单查询,肯定无法满足各种业务要求,我们需要更强大的查询支持,特别是根据不同条件拼接不同语句。
上面是两个非常典型的业务查询。
这里请出了条件表达式WhereExpression,实际上它只有两个功能,&表示And,|表示Or,根据表达式级别支持括号运算。
exp&=xxx 是最常用的写法,右边一般是各种Field表达式。
上面第一个例子,生成的查询语句可能是 select * from Student where classid=?classid and name like '%?key%'
为什么说“可能”?因为classid为0,或者key为空时,并不会参与拼接查询语句。
第二个例子稍微复杂一些,首先对key进行精确查询,找到了就返回,若是没找到,则开启模糊查询。
这里遇到了等于、包含、区间等判断操作,后面会详解所有支持的操作。
如非必要,建议保留select * 的查询方式,而不是指定列。
码农法则:数据库压力小于100qps时不要考虑指明select列来优化,大多数系统活不到需要优化的明天!
高级分页
两个例子都出现了一个PageParameter参数page,这是分页参数,包含分页查询以及排序所需要的数据。
PageIndex和PageSize指定页序号和每页大小,这是内部建立分页查询的核心依据;
Sort 指定排序字段,Desc 指定是否降序(默认升序);
RetrieveTotalCount 指定是否或者总记录数,若为true,则在查询记录集之前,先查询满足条件的总行数TotalCount,用于分页PageCount。此时等于执行两次数据库查询;
RetrieveState 指定是否获取统计 State,若为true,则在查询记录集之后,执行聚合查询,对数字型字段使用Sum聚合。此时最多可能执行3次数据库查询;
在执行FindAll查询时,若有传入 PageParameter 且 RetrieveTotalCount 为true,则先查询满足条件的记录数,大于0时才查某一页数据。
如果 Meta.Count 评估认为本表总行数超过100万,且FindAll查询没带有条件,则page.TotalCount直接取Meta.Count(少量偏差),以避免极大的FindCount耗时。
100万行以上数据表,如若不带条件或者条件没有命中索引,select count 将会极其的慢,在1000万以上甚至查不出来,这是XCode能对100亿表进行分页查询的关键所在。
Meta.Count 的初始值来自于数据库元数据索引表,里面有该表主键的总行数,取得该值后如果小于100万再异步select count一次。
10多年前博客园ORM大战的时候,我们常说,等你支持千万级分页的时候再来比,就是钻了select count很慢的这个空子,很多人count出来总数再分页 ^_^
上图4亿数据,查询第10000页,在SQLite单表上,阿里云1C1G服务器。
FindCount 分页
在早期版本,不支持RetrieveTotalCount ,只能通过 FindCount 取得满足该条件的总记录数,然后进行分页,至今仍然支持传统方法。
因此可以看到,FindAll 和 FindCount 都是成对出现,参数一摸一样。
并且 FindCount 方法也会带有分页参数,虽然用不到,但.NET2.0时代的 ObjectDataSource 要求两者的参数名称和顺序必须一致。
所有 FindCount 方法,将会得到 select count 查询语句,因此千万级大表需要慎用。
PageSplit 分页
内置支持的各种数据库,都有实现普通查询语句转为分页语句的 PageSplit(sql, start, maxNums) 方法。
MySql/SQLite/PostgreSQL 能够很好支持,只需要在 sql 后加上 limit start, maxNums 即可;
Oracle/SqlServer/Access/SqlCe 则要麻烦一次,其中SqlServer最复杂,不同版本的分页方法还不同,早期版本还要求有主键字段;
因此,sql 必须是简单的单表查询语句,PageSplit 才能把任意查询拆开并转换为分页查询。
大表分页优化
大表分页查询,开头会很快,越是往后越慢!
XCode采用倒置优化法,对于超过100万行(借助Meta.Count评估)的表,如果查询页超过中线,则从另一个方向查询,然后再把结果倒置回来。
XCode要求数据查询必须考虑分页,没有分页的系统一般死在100万行以内。
Field扩展
内嵌类_引用的字段是Field,它继承自FieldItem。
Field/FieldItem全部功能:
- Equal 等于,操作符==
- NotEqual 不等于,操作符!=
- 大于操作符>,大于等于>=
- 小于操作符<,小于等于<=
- StartsWith 字符串开始,like '{0}%'。(支持索引)
- EndsWith 字符串结束,like '%{0}'
- Contains 字符串包含,like '%{0}%'
- In 集合包含,支持列表集合、字符串子查询和SelectBuilder子查询,集合只有一个元素时转为相等操作
- NotIn 集合不包含,支持列表集合、字符串子查询和SelectBuilder子查询,集合只有一个元素时转为不相等操作
- IsNull 是否空
- NotIsNull 不是空
- IsNullOrEmpty 字符串空或零长度
- NotIsNullOrEmpty 字符串非空非零长度
- IsTrue 是否True或者False/Null,参数决定两组之一
- IsFalse 是否False或者True/Null,参数决定两组之一
- Between 时间区间,大于等于开始,小于结束,如果开始结束都只有日期而没有时分秒,则结束加一天,如(2019-04-17, 2019-04-17)查 time>='2019-04-17' and time<2019-04-18'
排序字句/分组聚合
- Asc,升序
- Desc,降序。order by name desc
- GroupBy,分组。group by name
- As,聚合别名
- Count,计数
- Sum,求和
- Min,最小
- Max,最大
查询的本质
查询的本质是五参数版FindAll(where, order, selects, start, maxnums),其它查询方式都由它转化而来!
Entity实体基类封装了各种常用的查询方法:
对于单表查询的XCode来说,五参数版FindAll很容易得到 select [selects] from [table] where [where] order by [order] limit [start], [maxnums] 语句,根据这个理念,FindAll可以支持任意复杂查询!
最终查询语句,由SelectBuilder类承载。
多表子查询
XCode不支持多表Join关联,这在前面《扩展属性》中提到过。
扩展属性固然可以解决关联多表字段的问题,并且借助缓存性能还不错,但是需要同时在两张表上设置条件的时候,就行不通了。
于是,需要用到高级查询,可以用子查询 来替代,正是前面说到的FieldItem.In扩展。
要查询名为“992班”的所有学生,一般这样写:
- select * from student s inner join class c on s.classid=c.id where c.name='992班'
XCode从2008年起,就放弃支持多表关联,自然也就不支持这样的写法。
在一般系统里面,班级表数据不多,可以借助实体缓存或者对象缓存:
- // Class.FindByName 内部用缓存
- var cls = Class.FindByName("992班");
- var list = Student.FindAll(Student._.ClassID==cls.ID);
- // select * from student where classid=1234
但如果主表从表都是百万级大表,或者从表查询条件比较复杂,缓存就有点难以为继了。
于是有了子查询:
调用方法:var list = Student.Search(SexKinds.女, "992班", p);
得到结果:
- select * from student where sex=2 and classid in(select id from class where name='992班')
至此,绝大部分多表关联复杂查询语句,可以转化为子查询 !
系列教程
NewLife.XCode教程系列[2019版]
- 增删改查入门。快速展现用法,代码配置连接字符串
- 数据模型文件。建立表格字段和索引,名字以及数据类型规范,推荐字段(时间,用户,IP)
- 实体类详解。数据类业务类,泛型基类,接口
- 功能设置。连接字符串,调试开关,SQL日志,慢日志,参数化,执行超时。代码与配置文件设置,连接字符串局部设置
- 反向工程。自动建立数据库数据表
- 数据初始化。InitData写入初始化数据
- 高级增删改。重载拦截,自增字段,Valid验证,实体模型(时间,用户,IP)
- 脏数据。如何产生,怎么利用
- 增量累加。高并发统计
- 事务处理。单表和多表,不同连接,多种写法
- 扩展属性。多表关联,Map映射
- 高级查询。复杂条件,分页,自定义扩展FieldItem,查总记录数,查汇总统计
- 数据层缓存。Sql缓存,更新机制
- 实体缓存。全表整理缓存,更新机制
- 对象缓存。字典缓存,适用用户等数据较多场景。
- 百亿级性能。字段精炼,索引完备,合理查询,充分利用缓存
- 实体工厂。元数据,通用处理程序
- 角色权限。Membership
- 导入导出。Xml,Json,二进制,网络或文件
- 分表分库。常见拆分逻辑
- 高级统计。聚合统计,分组统计
- 批量写入。批量插入,批量Upsert,异步保存
- 实体队列。写入级缓存,提升性能。
- 备份同步。备份数据,恢复数据,同步数据
- 数据服务。提供RPC接口服务,远程执行查询,例如SQLite网络版
- 大数据分析。ETL抽取,调度计算处理,结果持久化
[NewLife.XCode]高级查询(化繁为简、分页提升性能)的更多相关文章
- Linq高级查询,分页查询及查询分页结合
一.高级查询与分页查询 1.以...开头 StartsWith Repeater1.DataSource=con.Users.Where(r=>r.Nickname.StartsWith( ...
- [NewLife.XCode]高级增删改
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netstandard,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示 ...
- [NewLife.XCode]数据层缓存(网站性能翻10倍)
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示例代码和 ...
- java整合Elasticsearch,实现crud以及高级查询的分页,范围,排序功能,泰文分词器的使用,分组,最大,最小,平均值,以及自动补全功能
//为index创建mapping,index相当于mysql的数据库,数据库里的表也要给各个字段创建类型,所以index也要给字段事先设置好类型: 使用postMan或者其他工具创建:(此处我使用p ...
- 测试使用索引库crud和高级查询分页
1.搭建ES的服务 导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifa ...
- springboot整合Mangodb实现crud,高级查询,分页,排序,简单聚合
//linux安装mangodb教程:https://www.cnblogs.com/yangxiaohui227/p/11347832.html 1.引入maven 依赖 <dependenc ...
- 2.mysql高级查询
01.SQL高级查询_排序 1.排序语句:order by 排序字段名 asc(默认的-升序) / desc(降序); 2.例如:查询所有服装类商品,将查询结果以价格升序排序: ...
- [NewLife.XCode]扩展属性(替代多表关联Join提升性能)
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netstandard,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示 ...
- [NewLife.XCode]实体列表缓存(最土的方法实现百万级性能)
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示例代码和 ...
随机推荐
- mybatis递归查询
<!--mybatis递归查询--><resultMap id="recursionMenuMap" type="AgentMenu" ext ...
- HTML的Tomcat
修改D:\software\apache-tomcat-8.0.44\webapps\ROOT\WEB-INF\web.xml: <?xml version="1.0" en ...
- linux从0开始----01
1.VMware 虚拟机安装与卸载 推荐安装较高版本,11.x以后的.本课程安装12.x版本,需要序列号. 2.在vmware中安装centos客户机.初学者选择典型安装也可以. 1.vware文件菜 ...
- shiro简单配置 (写的不错 收藏一下)
抄袭的连接:https://blog.csdn.net/clj198606061111/article/details/24185023 注:这里只介绍spring配置模式. 因为官方例子虽然中有更加 ...
- ajax轮询与长轮询
刚刚网了关于轮询的知识,必须拿到自己这里来做个备份了! 其实以前用ajax轮询做个及时数据更新的,只是当时做了不知道那个就是轮询. 首先我们什么时候会想到用轮询技术呢? 一般而言,最多的是及时信息 ...
- extjs__(grid Panel绑定数据)
1.修改面板名称 双击My Panel 就可以进行修改 2拖入一个grid panel绑定数据 3.创建一个model 只是为了创建一个模型 相当于java中的模型层 只是数据的一个标准 4 ...
- Spring Boot中Web应用的统一异常处理 转载来自翟永超
我们在做Web应用的时候,请求处理过程中发生错误是非常常见的情况.Spring Boot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来 ...
- JS canvas 画板 撤销
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- win7 docker Toolbox 启动Docker Quickstart Terminal 失败!
解决办法: 在windows下安装docker Toolbox 启动Docker Quickstart Terminal 失败! 主要是用如下文件启动,临时解决,或设置环境变量
- IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?
1.前言 IM的群聊消息,究竟存1份(即扩散读方式)还是存多份(即扩散写方式)? 上一篇文章<IM群聊消息的已读回执功能该怎么实现?>是说,“很容易想到,是存一份”,被网友们骂了,大家争论 ...