数据库现有数据其中两列: s - 开始时间, e - 结束时间. 在新插入数据s', e'之前需要判断两个时间之间是否有重合

因为使用mybatis-plus的缘故, 结论都使用s或e在符号前面.

1. s < e

比如yyyy-MM-dd HH:mm:ss格式的数据, 多用于判断预约时间和每日排班冲突.

对于冲突的情况使用列举法有

  • s' < e' < s < e: 新时间段在已有时间左边, 不包含, 情况1
  • s' < s < e' < e: 新时间段和已有时间左边有交集, 情况2
  • s < s' < e' < e: 新时间段在已有时间内, 被包含关系, 也即在已有时间段内部, 情况3
  • s < s' < e < e': 新时间段和已有时间右边有交集, 情况4
  • s' < s < e < e': 新时间和已有时间是是包含关系, 也即新时间段在已有时间段外部, 情况5
  • s < e < s' < e': 新时间段在已有时间右边, 不包含, 情况6

    除去开始和最后的不包含, 可以得到当s < e' 并且 e > s'时候两个时间端肯定有交集, 也即冲突.

2. 存在s > e

比如HH:mm:ss格式的, 多用于固定早中晚班定义 / 营业时间等周期性活动的时间冲突判断.

2.1 分类逐步分析

2.1.1 s < e

  • s' < e': 同1, 判断s < e' 并且 e > s'即可
  • s' > e': 也即跨天的时候, 分成s' - 240 - e'
    • 前半段: 同1; (同下面24肯定大于s, 逻辑上可以与第一种情况写到一起)
    • 下半段: 0 < e, 只需要判断s < e'即可

2.1.2 s > e

同理拆分成s - 24 和 0 - e.

  • s' < e': 不跨天

    • 前半段: 24肯定大于s', 只需要判断s < e'
    • 后半段: 0肯定小于e', 只需要判断e > s'
  • s' > e': 跨天, 拆分成s'-24 和 0-e' (这种必有24, 答案恒真, 还是得翻译成代码)
    • 前本段: s - 24

      • s' - 24: 终点一样, 只判断s < s'即可
      • 0 - e': 0 < 24, 只需要判断s < e'即可
    • 后半段: 0 - e.
      • s' - 24: 0 < 24, 只需要判断e > s'即可
      • 0 - e': 起点一样, 只判断e > e'即可

2.1.3 代码

对于s > e情况下, 虽然sql可以使用start_time > end_time, 但是在mybatis-plus中写不出来

所以在保存时候添加一列is_greater代表是否当条数据跨天:

// 保存跨天时候的结束时间, 将结束时间变成24:00:00
// 传过来的结束时间赋值给nextDayEndTime, 也即finalNextDayEndTime
String finalNextDayEndTime = nextDayEndTime;
List<BaseTimeConfig> existList = configService.lambdaQuery()
.and(p -> p
// 按照开始小于结束判断(数据也是开始小于结束)
.and(q -> q.eq(BaseTimeConfig::getIsGreater, 0)
.and(q1 -> q1
.lt(BaseTimeConfig::getStartTime, endTime)
.gt(BaseTimeConfig::getEndTime, startTime))
// 开始大于结束时间时候(), 计算拆分出来的第二天也即0点 - endTime(复制给了nextDayEndTime)
// 需要((开始小于结束 和 开始大于结束拆分出第一天) or (开始大于结束拆分出第二天))满足其一即可
// 拆分出来的第二天是0开始, 结束时间肯定肯定比0大, 所以判断开始时间比nextDayEndTime小即可
.or(!"".equals(finalNextDayEndTime), q1 -> q1.lt(BaseTimeConfig::getStartTime, finalNextDayEndTime)))
// 按照开始小于结束判断(数据是开始大于结束的, 判断结束时间大于传入的开始时间即可,
// 因为数据库开始时间应该算0, 0肯定比传入的开始时间小于)
.or(q -> q.eq(BaseTimeConfig::getIsGreater, 1)
.and(q1 -> q1
// 后半截
.and(q2 -> q2.gt(BaseTimeConfig::getEndTime, startTime))
// 前半截
.or(q2 -> q2.lt(BaseTimeConfig::getStartTime, endTime))
// 开始大于结束时间时候
.or(!"".equals(finalNextDayEndTime), q2 -> q2.lt(BaseTimeConfig::getStartTime, finalNextDayEndTime)
.or(q3 -> q3.lt(BaseTimeConfig::getEndTime, finalNextDayEndTime))))))
.ne(baseTimeConfig.getId() != null && baseTimeConfig.getId() > 0,
BaseTimeConfig::getId, baseTimeConfig.getId())
.isNull(BaseTimeConfig::getDeletedAt)
.list();

2.1.4 总结

总体思路清晰, 逐步分析得到, 但不巧妙

2.2 预处理

2.2.1 提出假设

在保存s和e时候, 如果是跨天的拆分成s,24,0,e; 不跨天的话使用s,e,s,e, 对应字段s,e,s1,e1

对于s'和e'来说也是同上, 拆分出s',e',s1',e1'

分别比较s,e和s',e's1,e1和s1',e1', 两个条件使用或者连接, 也即: ((s < e' 并且 e > s') 或者 (s1 < e1' 并且 e1 > s1'))

2.2.2 分情况论证

  • 当两个都是跨天时候

    • 因为e,s1,e',s1'分别是24,0,24,0
    • 所以式子可以写为: ((s < 24 并且 24 > s') 或者 (0 < e1' 并且 e1 > 0))
    • 进一步得出: 两边都恒真, 也即两个都跨天时候肯定有重合的.
    • 0点肯定在的, 故成立. 见2.1.2下第二条
  • 当只s,e跨天时候
    • 因为e,s1,s1',e1'分别是24,0,s',e'
    • 所以式子可以写为: ((s < e' 并且 24 > s') 或者 (0 < e' 并且 e1 > s'))
    • 进一步可以得出: 只需要判断(s < e') 或者 (e1 > s')即可
    • 数据库跨天, 传入不跨, 见2.12下的第一条(e1始终是2.1中的e)
  • 当只s',e'跨天时候
    • 因为s1,e1,e',s1'分别是s,e,24,0
    • 所以式子可以写为: ((s < 24 并且 e > s') 或者 (s < e1' 并且 e > 0))
    • 进一步可以得出: 只需要判断(s < e1') 或者 (e > s')即可
    • 数据库不跨, 传入跨, 见2.1.1的第二条(e1'始终是2.1中的e')
  • 当都不跨天时候
    • 因为s1,e1,s1',e1'分别是s,e,s',e'
    • 所以式子可以写为: ((s < e' 并且 e > s') 或者 (s < e' 并且 e > s'))
    • 进一步可以得出: 只需要判断s < e' 并且 e > s'即可
    • 都不跨天, 也即都增, 见1 或 2.1.1的第一条
  • 综上, 在保存se时候, 如果保存成四个字段, 如果跨天: s,24,0,e; 如果不跨天就s,e,s,e; 同理s',e'也相同规则拆分开

    使用前面两个和前面两个比较, 后面两个与后面两个比较的结论成立.

    (一个跨天, 一个不跨天时候, 使用不跨天的必须要与跨天的两端比较, 那就不跨天时候存两遍, 这样就都比较了)

2.2.3 代码

// s', e'
String startTime = "22:00:00", endTime = "24:00:00";
// s1', e1'
String startTime1 = "00:00:00", endTime1 = "03:00:00"; // ((s < e' 并且 e > s') || (s1 < e1' 并且 e1 > s1'))
configService.lambdaQuery()
.and(q -> q
.lt(BaseTimeConfig1::getS, endTime)
.gt(BaseTimeConfig1::getE, startTime))
.or(q -> q
.lt(BaseTimeConfig1::getS1, endTime1)
.gt(BaseTimeConfig1::getE1, startTime1))
.list();

数据库sql中处理时间冲突问题的更多相关文章

  1. 数据库——SQL中EXISTS怎么用2(转)

    数据库sql语句的exists总结 sql exists in 学习 先来比较下语法: --deals=交易表,areas=地域表,例如香港:我们的目的:查看有交易的地域 select * from ...

  2. 数据库SQL中case when函数的用法

    Case具有两种格式,简单Case函数和Case搜索函数.这两种方式,可以实现相同的功能.简单Case函数的写法相对比较简洁,但是和Case搜索函数相比,功能方面会有些限制,比如写判断式. 简单Cas ...

  3. 数据库sql中distinct用法注意事项

    在写sql中去重复等操作,需要用到distinct. 在使用distinct的时候要注意,尤其是在有行列转换的时候.要把sql运行出来看看是不是与你想要的结果一样. 通过自己试验,distinct有从 ...

  4. [数据库]SQL中Group By 的常见使用方法.

    前言今天逛java吧看到了一个面试题, 于是有了今天这个文章, 回顾下Group By的用法.题目如下:Select name from table group by name having coun ...

  5. 数据库SQL中Like的用法总结

    最先做项目多次用到LIKE语句,下面总结下经常用到了. 语法 match_expression [ NOT ] LIKE pattern [ ESCAPE escape_character ] 参数 ...

  6. 数据库——SQL中EXISTS怎么用3(转)

    有一个查询如下: 1 SELECT c.CustomerId, CompanyName   2 FROM Customers c   3 WHERE EXISTS(   4     SELECT Or ...

  7. 数据库——SQL中EXISTS怎么用1(转)

    EXISTS用于检查子查询是否至少会返回一行数据,该子查询实际上并不返回任何数据,而是返回值True或False 方法/步骤   EXISTS用于检查子查询是否至少会返回一行数据,该子查询实际上并不返 ...

  8. SQL中查看数据库各表的大小

    SQL中查看数据库各表的大小 编写人:CC阿爸 2014-6-17 在日常SQL数据库的操作中,如何快速的查询数据库中各表中数据的大小. 以下有两种方法供参考: 第一种: create table # ...

  9. SQL中删除某数据库所有trigger及sp

    SQL中删除某数据库所有trigger及sp   编写人:CC阿爸 2014-6-14 在日常SQL数据库的操作中,如何快速的删除所有trigger及sp呢 以下有三种方式可快速处理. --第一种 - ...

  10. 转载-SQL中的where条件,在数据库中提取与应用浅析

    1        问题描述 一条SQL,在数据库中是如何执行的呢?相信很多人都会对这个问题比较感兴趣.当然,要完整描述一条SQL在数据库中的生命周期,这是一个非常巨大的问题,涵盖了SQL的词法解析.语 ...

随机推荐

  1. 生信服务器 | 更改 CentOS/RHEL 6/7 中的时区

    这几天在学习折腾 docker 的时候遇到一个很常见的问题,就是 run container 的时候发现大部分 image 默认使用的时间都是 UTC  (Universal Time Coordin ...

  2. STM32 + RT-Thread + LwIp + DM9000

    一.概述 开发板:STM32F103ZET6(战舰) RT-Thread:5.0.0 LwIp:2.1.2 网卡芯片:DM9000 编译环境:keil 我简单了解了一下,在嵌入式中,网络芯片的使用方式 ...

  3. CentOS7 本地光盘镜像rpm包

    CentOS7 本地光盘镜像rpm包 一.前言 rpm包的下载方式 通过本地光盘镜像下载rpm,centos7.iso镜像文件,内置了绝大多数软件的rpm包(本文章即演示如何配置本地rpm) 在线下载 ...

  4. Vue3从入门到精通(三)

    vue3插槽Slots 在 Vue3 中,插槽(Slots)的使用方式与 Vue2 中基本相同,但有一些细微的差异.以下是在 Vue3 中使用插槽的示例: // ChildComponent.vue ...

  5. 《最新出炉》系列初窥篇-Python+Playwright自动化测试-5-元素定位大法-上篇

    1.简介 说到元素定位,小伙伴或者童鞋们肯定会首先想到 selenium 的八大元素定位大法.同理Playwright也有自己的元素定位的方法.今天就给小伙伴或者童鞋们讲解和分享一下Playwrigh ...

  6. 暗黑2能用Java开发?还能生成APP?

    最近烧哥发现个宝藏项目,竟然用Java开发了暗黑2出来. 众所周知,暗黑2是暴雪开发的一款经典游戏,距今虽有20多年,仍然有很多粉丝. 粉丝延续热情的方式有很多,一种是做Mod,比如魔电,对怪物.技能 ...

  7. 用字符串表达式执行引擎消除掉if else if

    背景 最近我搞了个微信机器人,@机器人 xxx 这样来发送命令 能拿到的信息有,消息内容,消息发送人,消息所在的群id等 需要根据消息内容或者消息发送群id等不同的条件组合来决定走哪个处理逻辑. 简单 ...

  8. LAL v0.36.7发布,Customize Sub,我有的都给你

    Go语言流媒体开源项目 LAL 今天发布了v0.36.7版本. LAL 项目地址:https://github.com/q191201771/lal 老规矩,简单介绍一下: ▦ Customize S ...

  9. kbuild系统中最简单的目标 -- help(三)

    当你学完help目标后你会发现它与我们的代码基本没有什么直接的关系,仅仅是用来生成kbuild的简短使用说明,但是用它来作为学习掌握kbuild编译系统的开篇,不管是从难度还是学习感受上真的是再适合不 ...

  10. 无需学习Python,一个公式搞定领导想看的大屏

    摘要:本文由葡萄城技术团队于博客园原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 不要让"做不了"成为数字化转型的障碍 随着 ...