MySQL 定义连续值范围的开始点和结束点

备注:测试数据库版本为MySQL 8.0

一.需求

mysql> select * from v;
±--------±-----------±-----------+
| proj_id | proj_start | proj_end |
±--------±-----------±-----------+
| 1 | 2020-10-01 | 2020-10-02 |
| 2 | 2020-10-02 | 2020-10-03 |
| 3 | 2020-10-03 | 2020-10-04 |
| 4 | 2020-10-04 | 2020-10-05 |
| 5 | 2020-10-06 | 2020-10-07 |
| 6 | 2020-10-16 | 2020-10-17 |
| 7 | 2020-10-17 | 2020-10-18 |
| 8 | 2020-10-18 | 2020-10-19 |
| 9 | 2020-10-19 | 2020-10-20 |
| 10 | 2020-10-21 | 2020-10-22 |
| 11 | 2020-10-26 | 2020-10-27 |
| 12 | 2020-10-27 | 2020-10-28 |
| 13 | 2020-10-28 | 2020-10-29 |
| 14 | 2020-10-29 | 2020-10-30 |
±--------±-----------±-----------+

希望得到如下结果集
±---------±-----------±-----------+
| proj_grp | proj_start | proj_end |
±---------±-----------±-----------+
| 1 | 2020-10-01 | 2020-10-05 |
| 2 | 2020-10-06 | 2020-10-07 |
| 3 | 2020-10-16 | 2020-10-20 |
| 4 | 2020-10-21 | 2020-10-22 |
| 5 | 2020-10-26 | 2020-10-30 |
±---------±-----------±-----------+

二.解决方案

首先,必须识别它们的范围。proj_start和proj_end的值定义了一行的范围,要把某行看做“连续的”或者属于一个组,
则它的PROJ_START值一定要等于它前一行的PROJ_END值。

如果某个行的PROJ_START值不等于前一行的PROJ_END值,而且它的PROJ_END值也不等于下一行的PROJ_START值,则它就是一个单独的组。

确定了范围之后,就需要将属于同意范围的行划成同一组,并且只返回组开始和结束的点。

create view v2
as
select a.*,
       case 
          when (
             select b.proj_id
                from v b
              where a.proj_start = b.proj_end
                )
               is not null then 0 else 1
        end as flag
   from v a;


select * from v2;

select proj_grp,
       min(proj_start) as proj_start,
       max(proj_end) as proj_end
   from (
select a.proj_id,a.proj_start,a.proj_end,
       (select sum(b.flag)
           from v2 b
        where b.proj_id <= a.proj_id) as proj_grp
      from v2 a
      ) x
group by proj_grp;

测试记录

mysql>
mysql> create view v2
    -> as
    -> select a.*,
    ->        case
    ->           when (
    ->              select b.proj_id
    ->                 from v b
    ->               where a.proj_start = b.proj_end
    ->                 )
    ->                is not null then 0 else 1
    ->         end as flag
    ->    from v a;
Query OK, 0 rows affected (0.01 sec)

mysql>
mysql> select * from v2;
+---------+------------+------------+------+
| proj_id | proj_start | proj_end   | flag |
+---------+------------+------------+------+
|       1 | 2020-10-01 | 2020-10-02 |    1 |
|       2 | 2020-10-02 | 2020-10-03 |    0 |
|       3 | 2020-10-03 | 2020-10-04 |    0 |
|       4 | 2020-10-04 | 2020-10-05 |    0 |
|       5 | 2020-10-06 | 2020-10-07 |    1 |
|       6 | 2020-10-16 | 2020-10-17 |    1 |
|       7 | 2020-10-17 | 2020-10-18 |    0 |
|       8 | 2020-10-18 | 2020-10-19 |    0 |
|       9 | 2020-10-19 | 2020-10-20 |    0 |
|      10 | 2020-10-21 | 2020-10-22 |    1 |
|      11 | 2020-10-26 | 2020-10-27 |    1 |
|      12 | 2020-10-27 | 2020-10-28 |    0 |
|      13 | 2020-10-28 | 2020-10-29 |    0 |
|      14 | 2020-10-29 | 2020-10-30 |    0 |
+---------+------------+------------+------+
14 rows in set (0.00 sec)

mysql>
mysql> select proj_grp,
    ->        min(proj_start) as proj_start,
    ->        max(proj_end) as proj_end
    ->    from (
    -> select a.proj_id,a.proj_start,a.proj_end,
    ->        (select sum(b.flag)
    ->            from v2 b
    ->         where b.proj_id <= a.proj_id) as proj_grp
    ->       from v2 a
    ->       ) x
    -> group by proj_grp;
+----------+------------+------------+
| proj_grp | proj_start | proj_end   |
+----------+------------+------------+
|        1 | 2020-10-01 | 2020-10-05 |
|        2 | 2020-10-06 | 2020-10-07 |
|        3 | 2020-10-16 | 2020-10-20 |
|        4 | 2020-10-21 | 2020-10-22 |
|        5 | 2020-10-26 | 2020-10-30 |
+----------+------------+------------+
5 rows in set (0.00 sec)

mysql>
### MySQL Next-Key Lock 的范围及应用场景 #### 1. Next-Key Lock 的定义与作用 Next-Key Lock 是一种组合锁,由记录锁(Record Lock)间隙锁(Gap Lock)共同构成。它的主要目的是在事务隔离级别为可重复读(Repeatable Read)时,防止幻读现象的发生[^4]。 #### 2. Next-Key Lock 的加锁范围 Next-Key Lock 的锁定范围不仅包括具体的索引记录本身,还包括这些记录之间的间隙部分。这意味着当数据库执行某些操作时,InnoDB 不仅会对实际的数据行上锁,还会对相邻的空白区域施加限制,从而阻止其他事务在此范围内插入新数据[^3]。 对于一张带有唯一索引或主键索引的表 `t` 来说,在执行类似 `SELECT ... FOR UPDATE` 或者 `INSERT INTO t (...) VALUES (...)` 这样的语句时,如果涉及到多个可能满足条件的区间,则整个查询所覆盖的所有连续区间的边界都会被加上 Next-Key 锁定标记[^2]。 例如考虑这样一个场景:假设有一个简单的整数列作为主键,并且当前存在两条记录分别为 id=1 id=5 。那么在这两个已知之间就形成了一个潜在可以插入其它数的空间 (即 gap),而此时如果我们运行了一个带有限制条件并请求独占访问权限(`FOR UPDATE`) 的 SELECT 查询命令指向这个特定区域内不存在但符合条件的位置(比如 WHERE id BETWEEN 2 AND 4 ) ,则系统会自动对该段落应用 next-key locking —— 即同时保护了现有的节(id=1, id=5)及其间所有的空位[(id>1 and id<5)]不受外部干扰直至本笔交易完成为止[^5]。 #### 3. 应用场景分析 - **防止幻读**: 在 RR 隔离级别下,通过使用 Next-Key Locks 可以有效地避免同一事务内的多次相同查询返回不同的结果集的情况发生,这是因为它能够阻塞那些试图修改或者新增进入现有检索结果集合内部动作的行为直到前者的结束时刻到来之前都不会允许后者成功提交更改申请。 - **提高并发性能的同时保持一致性**: 当多用户环境下的应用程序频繁地针对某个业务对象进行增删改查活动期间,合理运用此类锁定策略有助于平衡资源利用率与安全性需求两者之间的矛盾关系——既能让尽可能多的操作实例平行推进下去又不会牺牲整体系统的稳定性可靠性标准。 ```sql -- 示例 SQL BEGIN; SELECT * FROM t WHERE id >= 2 AND id <= 4 FOR UPDATE; -- 对 [2, 4] 范围内的所有记录以及它们之前的间隙加锁 COMMIT; ``` 上述代码片段展示了如何利用 `FOR UPDATE` 实现基于 Next-Key Lock 的显式锁定行为。这里得注意的是,即使在这个指定条件下没有任何匹配到的实际行项目也会触发相应的锁定效果以便维护后续逻辑处理过程中的预期状态不变性约束要求得到满足。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值