问题描述
有没有一种简单的方法可以避免使用游标来转换它:
+-------+------+-------+
| Group | From | Until |
+-------+------+-------+
| X | 1 | 3 |
+-------+------+-------+
| X | 2 | 4 |
+-------+------+-------+
| Y | 5 | 7 |
+-------+------+-------+
| X | 8 | 10 |
+-------+------+-------+
| Y | 11 | 12 |
+-------+------+-------+
| Y | 12 | 13 |
+-------+------+-------+
对此:
+-------+------+-------+
| Group | From | Until |
+-------+------+-------+
| X | 1 | 4 |
+-------+------+-------+
| Y | 5 | 7 |
+-------+------+-------+
| X | 8 | 10 |
+-------+------+-------+
| Y | 11 | 13 |
+-------+------+-------+
到目前为止,我已经尝试为每行分配一个ID,并为该ID分配GROUP BY,但是如果不使用游标,我将无法获得更近的距离。
解决方法
SELECT `Group`,`From`,`Until`
FROM ( SELECT `Group`,ROW_NUMBER() OVER (PARTITION BY `Group` ORDER BY `From`) rn
FROM test t1
WHERE NOT EXISTS ( SELECT NULL
FROM test t2
WHERE t1.`From` > t2.`From`
AND t1.`From` <= t2.`Until`
AND t1.`Group` = t2.`Group` ) ) t3
JOIN ( SELECT `Group`,`Until`,ROW_NUMBER() OVER (PARTITION BY `Group` ORDER BY `From`) rn
FROM test t1
WHERE NOT EXISTS ( SELECT NULL
FROM test t2
WHERE t1.`Until` >= t2.`From`
AND t1.`Until` < t2.`Until`
AND t1.`Group` = t2.`Group` ) ) t4 USING (`Group`,rn)
必须在任何重叠类型(部分重叠,相邻,完全包含)下工作。
如果From
和/或Until
为NULL,将不起作用。
您可以添加英文说明吗? – ysth
第一个子查询搜索加入的范围开始(请参见小提琴-它是单独执行的)-它在不位于任何其他范围的中间/结尾的组中搜索From
值(允许起点相等)
第二个子查询对连接的范围Until
执行相同的操作。
两者都枚举发现值递增。
外部查询只是将每个范围的起点和终点连接成一行。
,如果使用的是MYSQL 8+版本,则可以使用row_number获得所需的结果:
SELECT MIN(`FROM`) START,MAX(`UNTIL`) END,`GROUP` FROM (
SELECT A.*,ROW_NUMBER() OVER(ORDER BY `FROM`) RN_FROM,ROW_NUMBER() OVER(PARTITION BY `GROUP` ORDER BY `UNTIL`) RN_UNTIL
FROM Table_lag A) X
GROUP BY `GROUP`,(RN_FROM - RN_UNTIL)
ORDER BY START;
,
我将为此使用递归CTE:
JavaScript Events
cable-ready:before-morph
cable-ready:after-morph
锚表达式(DateTimeFormatter isoFormat = ISODateTimeFormat.dateTimeParser();
Date date = isoFormat.parseDateTime("2020-09-09T09:58:00+0000").toDate();
// Wed Sep 09 05:58:00 EDT 2020
)查找所有&组合,而不会与之组合,因此(最终输出中每一行都有一行):
然后递归查询为我们的每一行添加合并间隔的行。
然后按组和从(这些列的名称很糟糕)进行分组,以获取最大的
每个起始组/起始间隔。
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=9efa508504b80e44b73c952572394b76
或者,您可以使用一组简单的联接和子查询来完成此操作,而无需CTE或窗口函数:
with recursive intervals (`Group`,`Until`) as (
select distinct t1.Group,t1.From,t1.Until
from Table_lag t1
where not exists (
select 1
from Table_lag t2
where t1.Group=t2.Group
and t1.From between t2.From and t2.Until+1
and (t1.From,t1.Until) <> (t2.From,t2.Until)
)
union all
select t1.Group,t2.Until
from intervals t1
join Table_lag t2
on t2.Group=t1.Group
and t2.From between t1.From and t1.Until+1
and t2.Until > t1.Until
)
select `Group`,max(`Until`) as Until
from intervals
group by `Group`,`From`
order by `From`,`Group`;
(我将此处的列重命名为不需要反引号。)
这里有一个选择来获取我们将报告的可报告间隔的所有开始,再加入另一个类似的选择(您可以使用CTE以避免冗余)来查找同一组的可报告间隔的以下开始(如果有的话)。将其包装在子查询中,以获取以下可报告间隔的组,起始值和起始值。然后,它只需要联接在该范围内开始的所有其他记录并选择最大结束值。
https://dbfiddle.uk/?rdbms=mysql_5.5&fiddle=151cc933489c299f7beefa99e1959549
,您只能使用某些“孤岛”技术使用窗口函数来做到这一点。
想法是使用lag()
和窗口sum()
构建具有相同组和重叠范围的连续记录组。然后,您可以汇总组:
select grp,min(c_from) c_from,max(c_until) c_until
from (
select
t.*,sum(lag_c_until < c_from) over(partition by grp order by c_from) mygrp
from (
select
t.*,lag(c_until,1,c_until) over(partition by grp order by c_from) lag_c_until
from mytable t
) t
) t
group by grp,mygrp
您选择的列名称与SQL关键字(group
,from
)冲突,因此我将它们重命名为grp
,c_from
和c_until
。 / p>
Demo on DB Fiddle -首先归功于ysth,以创建小提琴:
grp | c_from | c_until :-- | -----: | ------: X | 1 | 4 Y | 5 | 7 X | 8 | 10 Y | 11 | 13