带有当前日期时间表达式条件的查询效率提升方法

简介: 针对含有日期表达式的计算字段参与到表达式查询从而导致系统效率低下的情况,通过代数转换来充分利用数据库索引达到提升效率的目的

一、问题背景

在实际的业务需求中,经常会遇到根据业务操作的当前时间对数据进行查询的场景,例如库存管理中对保质期的查询,以便了解即将会有多少商品即将超过保质期限,类似下图所示:

image.png

在这样的报表中,往往会存在增加一个计算字段的诉求,这个字段(临期天数)是在页面加载的时候通过系统计算自动得到的,因而在数据库中不会存储。

在这样的报表开发过程中,增加一个含有日期表达式的计算字段是一个常见的诉求,比如上图中的临期天数(expired_days),就是由“保质日期”(expiration_date)和“当前日期”(current_date())计算得到的,其计算公式为:

expired_days = expiration_date - current_date()

在报表展示的时候,该字段由计算引擎(前端js或者serverjava)实时算出expired_days的值之后展示在界面上,因此在后台数据库中也不会存储。

这时一个问题就浮出水面了:如何支持针对字段expired_days的高效过滤?这对高时效要求的业务来说是迫切需要解决的问题。

 

二、思考分析

思路一:条件直接拼接

直接把expired_days的公式提交给DB,假设要查询“3天内到期的数据”,过滤条件是:“expired_days ”<  3;直接把expiration_date  - current_date() 拼接到sqlwhere条件里提交给DB

这样会存在一个问题:由于对expiration_date字段引入了表达式,查询时没办法走索引,会导致查询效率很低下,在数据量很大的情况下,查询接口一定会超时。


思路二:条件转换

那如果在过滤的时候,由应用程序先把current_date()的值计算好之后,再提交给DB呢?比如:

expiration_date – ‘2021-8-17’ < 3

经测试,这种条件在mysqlpostgresql中,查询时同样不能使用expiration_date字段的索引,因而也不解决问题。


思路三:表达式索引

我们知道,在postgresqlmysql的高版本中已经支持了表达式索引,可不可以把expiration_date - current_date() 建立一个表达式索引呢?

这样做的结果也不行,因为这里的current_date()取值就是建立索引那一刻的值(即创建索引的DDL执行的时刻),而我们业务的实际需求是current_date()应该取查询过滤那一刻的值,不满足业务含义。


思路四:代数转换

我们的目标是在查询时能够使用到expiration_date字段的索引,而我们的过滤表达式为:

expiration_date – ‘2021-8-17’ < 3

通过前面的分析我们了解到,DB之所以不走expiration_date的索引的核心原因是:过滤表达式的左侧只能是expiration_date这个字段名,而不能是包含expiration_date的任何表达式,那么这样方案就出来了:我们可以由应用程序把上述表达式做一个简单的变化,变成以下形式:

expiration_date  <  3 + ‘2021-8-17’

然后再把这个表达式提交给DB,这样就能使用索引,实现高效查询了。

 

三、解决方案

最终我们选择思路四来提升查询的效率,其中关键的一步是做表达式的转换,这在数学上称为“代数转换”。幸运的是,Java方面已经有开源的 代数转换 的类库,我们只要集成到我们的服务里来就可以了(参考:https://github.com/axkr/symja_android_library)。另外,在Python中也有类似的类库,我们可以根据自己的语言需要选择使用。

 

四、效果对比

下面将对“代数转换”前后的查询性能进行一个对比测试验证。

验证测试条件

数据库

PostgreSQL

数据表

 inventory_store

数据量

2373w数据,其中expiration_date是时间戳字段,有索引

“代数转换”前

SQL脚本:

select  * from inventory_store

where expiration_date - 1630425600000 > 748800000

limit 10

执行结果:

image.png


耗时:15.354

结果分析:

   由于使用了全表扫描,未能使用索引,导致查询效率较低。


 “代数转换”后

SQL脚本:

select  * from inventory_store

where expiration_date > 1630425600000 + 748800000

limit 10

执行结果:

image.png



耗时:0.14 毫秒

结果分析:

   由于使用expiration_date索引,查询效率明显提升。

             结论:通过“代数转换”方式,在本测试案例中提升查询效率99%以上,能够有效解决带有当前日期时间表达式条件查询效率问题。

相关文章
|
14天前
|
SQL 数据挖掘 关系型数据库
SQL查询次数大于1的记录:高效技巧与方法
在数据库管理中,经常需要统计某些操作的次数,特别是当需要找出哪些记录或值出现的次数超过一定阈值(如大于1次)时
|
14天前
|
SQL 存储 数据库
SQL查询100以内数值的技巧与方法
在数据库操作中,经常需要查询特定范围内的数据,比如查询某个数值字段在100以内的记录
|
2月前
|
运维 监控 Serverless
函数计算产品使用问题之怎么查询在特定时间段内应用的调用次数
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
4月前
|
SQL DataWorks 数据处理
DataWorks产品使用合集之假设存在时间戳字段: 假设源表有一个记录数据更新时间的字段,如何设置过滤条件
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
23 1
|
5月前
|
C#
C#动态查询:巧用Expression组合多条件表达式
在C#中,利用`Expression`类和`AndAlso`、`OrElse`方法,可以组合两个`Expression&lt;Func&lt;T, bool&gt;&gt;`以实现动态多条件查询。该方法通过构建表达式树,方便地构建复杂查询。示例代码展示了如何创建表达式树,分别检查年龄大于等于18和姓名为&quot;John&quot;的条件,并使用`AndAlso`组合这两个条件,最终编译为可执行的委托进行测试。
205 1
|
Java
高效实现区间条件过滤:Java字符串转换为条件语句
高效实现区间条件过滤:Java字符串转换为条件语句
89 0
【SQL开发实战技巧】系列(十七):数据仓库中时间类型操作(初级)确定两个日期之间的工作天数、计算—年中周内各日期出现次数、确定当前记录和下一条记录之间相差的天数
如何确定两个日期之间的工作日有多少天、计算—年中每周内各日期出现次数、确定当前记录和下一条记录之间相差的天数【SQL开发实战技巧】这一系列博主当作复习旧知识来进行写作,毕竟SQL开发在数据分析场景非常重要且基础,面试也会经常问SQL开发和调优经验,相信当我写完这一系列文章,也能再有所收获,未来面对SQL面试也能游刃有余~。本章节的三个需求:确定两个日期之间的工作天数、计算—年中周内各日期出现次数、确定当前记录和下一条记录之间相差的天数有些许难度,不过建议还是学会比较好。
【SQL开发实战技巧】系列(十七):数据仓库中时间类型操作(初级)确定两个日期之间的工作天数、计算—年中周内各日期出现次数、确定当前记录和下一条记录之间相差的天数
使用lambda去重、map排序、按任意时间间隔(小时、半小时、分钟)进行结果统计
使用lambda去重、map排序、按任意时间间隔(小时、半小时、分钟)进行结果统计
248 0
使用lambda去重、map排序、按任意时间间隔(小时、半小时、分钟)进行结果统计
|
关系型数据库 MySQL
ES复杂操作-布尔值查询(多条件精确查询)
ES复杂操作-布尔值查询(多条件精确查询)
|
存储 Unix 关系型数据库
关于日期及时间字段的查询
在项目开发中,一些业务表字段经常使用日期和时间类型,而且后续还会牵涉到这类字段的查询。关于日期及时间的查询等各类需求也很多,本篇文章简单讲讲日期及时间字段的规范化查询方法。
264 0
关于日期及时间字段的查询