如何在mariadb中使用光标合并实体

t30tvxxf  于 2022-12-18  发布在  其他
关注(0)|答案(1)|浏览(113)

给出以下数据集:
| 开始日期|停止_日期|集团|
| - ------|- ------|- ------|
| 2010年2月14日|二○一○年十月二十日|1个|
| 二○一○年十月二十一日|二○一○年十一月十一日|1个|
| 2013年1月1日|2013年4月4日|第二章|
| 2013年2月2日|2011年5月5日|第二章|
我想合并属于同一组的日期,并基于:如果stop_dt + 1天=另一个元素的start_dt,或者如果start_dt在另一个元素的start_dt和stop_dt之间。
预期结果:
| 开始日期|停止_日期|
| - ------|- ------|
| 2010年2月14日|二○一○年十一月十一日|
| 2011年1月1日|2011年5月5日|
我可以通过处理整个数据集来实现这一点,但是,我想使用游标逐个元素地处理。用我目前的方法,我可以合并它们,但是我最终得到了其他不需要的记录(因为我正在进行双插入,而不是删除)

create procedure test_curser() 
begin
    DECLARE done BOOLEAN DEFAULT 0;
    DECLARE p_id BIGINT UNSIGNED;
    declare c1 cursor for
    select id from initial_table;
    DECLARE CONTINUE handler FOR SQLSTATE '02000'
    SET done = 1;
    open c1;
    fetch c1 into p_id;
        while not done do call sort(p_id);
            fetch c1 into p_id;
        end while;
    close c1;

create procedure sort(in p_id int)
begin
    insert into result(id, start_dt, stop_dt, grp) 
    (select id,start_dt, stop_dt, grp from initial_table where id = p_id);  
    
    # We check: If the table contain only one element then no merging is require, otherwise merge
    if (select count(id) from result) > 1 then
        insert into result(start_dt, stop_dt,grp) 
        (select if(r2.start_dt < r1.start_dt, r2.start_dt, r2.start_dt) as start_dt,
                if(r2.stop_dt > r1.stop_dt, r2.stop_dt, r1.stop_dt) as stop_dt,
                r1.grp
                from result as r1 join result as r2 
            on r2.grp = r1.grp and
            datediff(r2.stop_dt, r1.start_dt) = -1 or 
            (r1.start_dt between r2.start_dt and r2.stop_dt)
             where r1.id = p_id);
    else
        select 'process...';
    end if;
end

检查Demo
我使用的是10.4.12-MariaDB
谢谢。

oymdgrw7

oymdgrw71#

光标非常麻烦,并且增加了解决该任务的复杂性。解决该任务的更简化的方式包括:

  • 使用CASE +运行总和为每个连续(stop_dt,start_dt)值(相差一天)生成岛
  • 使用MINMAX聚合函数对“grp”字段和按标识的岛新建的分区应用聚合
WITH cte AS (
    SELECT *, CASE WHEN ADDDATE(LAG(stop_dt) OVER(PARTITION BY grp ORDER BY id), 1) = start_dt 
                   THEN 0 ELSE 1 END AS changed_value
    FROM initial_table
), cte2 AS (
    SELECT *, SUM(changed_value) OVER(PARTITION BY grp ORDER BY id) AS islands_partition
    FROM cte
)
SELECT MIN(start_dt) AS start_dt, 
       MAX(stop_dt)  AS stop_dt
FROM cte2
GROUP BY grp, islands_partition;

然后,您可以使用以下命令创建“result”表:

CREATE TABLE result_table AS 
WITH cte AS ...

检查here演示。

可选注解:为了更好地理解,我建议检查此查询的每一步的输出:

  1. SELECT * FROM cte
  2. SELECT * FROM cte2

相关问题