如果情节直接连续或重叠,则合并DATE行

问题描述

|| 我有一张这样的桌子:
ID    BEGIN    END
如果存在相同ID的重叠情节(例如
2000-01-01
-
2001-12-31
2000-06-01
-
2002-06-31
),我希望使用
MIN(BEGIN)
MAX(END)
合并行。 如果情节是直接连续的(例如
2000-01-01
-
2000-06-31
2000-07-01
-
2000-12-31
),则应执行相同的操作。 如果情节之间有“丢失”天(例如
2000-01-01
-
2000-06-15
2000-07-01
-
2000-12-31
),则不应合并它们。 如何做到这一点? 目前,我的代码如下所示:
SELECT \"ID\",MIN(\"BEGIN\"),MAX(\"END\")
FROM ...
GROUP BY \"ID\"
但是,当然,这不能满足最后一个条件(如果有“丢失”天,则不合并)。 先感谢您! [编辑] 我正在研究一个解决方案,我将表与自身连接在一起。这是一项改进,但尚未完成。我认为其他建议更好(但更复杂)。但是,我想分享我未完成的工作:
SELECT \"ID\",LEAST(tab1.\"BEGIN\",tab2.\"BEGIN\"),GREATEST(tab1.\"END\",tab2.\"END\")
  FROM <mytable> AS tab1
  JOIN <mytable> AS tab2
    ON tab1.\"ID\" = tab2.\"ID\"
    AND  (tab1.\"BEGIN\",tab1.\"END\" + INTERVAL \'2 day\') OVERLAPS (tab2.\"BEGIN\",tab2.\"END\")
  ORDER BY \"ID\"
[编辑2] 谢谢您的帮助! 我试图弄清楚窗口函数和WITH-QUERY到现在已经工作了几个小时-直到我意识到我的数据库在PostGreSQL 8.3上运行(这两个都不支持)。有没有窗口功能和WITH-QUERY的方法吗? 再次谢谢你! [编辑3] 样本数据:
ID        BEGIN         END
1;\"2000-01-01\";\"2000-03-31\"  
1;\"2000-04-01\";\"2000-05-31\"  
1;\"2000-04-15\";\"2000-07-31\"  
1;\"2000-09-01\";\"2000-10-31\"  
2;\"2000-02-01\";\"2000-03-15\"  
2;\"2000-01-15\";\"2000-03-31\"  
2;\"2000-04-01\";\"2000-04-15\"  
3;\"2000-06-01\";\"2000-06-15\"  
3;\"2000-07-01\";\"2000-07-15\"  
样本输出:
ID        BEGIN         END
1;\"2000-01-01\";\"2000-07-31\"
1;\"2000-09-01\";\"2000-10-31\"
2;\"2000-01-15\";\"2000-04-15\"
3;\"2000-06-01\";\"2000-06-15\"
3;\"2000-07-01\";\"2000-07-15\"
[编辑4] 一种可能的解决方案:
WITH
  t1 AS (
    SELECT id,begin AS time
      FROM \"nace-8510-test\".checkfkt
    UNION ALL
    SELECT id,end
      FROM \"nace-8510-test\".checkfkt
  ),t2 AS (
    SELECT Row_Number() OVER(PARTITION BY id ORDER BY time) AS num,id,time
      FROM t1 AS t1_1
  ),t3 AS (
    SELECT t2_1.num - Row_Number() OVER(PARTITION BY t2_1.id ORDER BY t2_1.time,t2_2.time) num1,t2_1.id,t2_1.time AS begin,t2_2.time AS end
      FROM t2 AS t2_1
        INNER JOIN t2 AS t2_2
          ON t2_1.id = t2_2.id
            AND t2_1.num = t2_2.num - 1
      WHERE
        EXISTS (
          SELECT *
            FROM \"nace-8510-test\".checkfkt AS s
            WHERE s.id = t2_1.id
              AND (s.begin < t2_2.time AND s.end > t2_1.time)
        )
        OR t2_1.time = t2_2.time
        OR t2_1.time + INTERVAL \'1 day\' = t2_2.time
  )

SELECT id,MIN(begin) AS von,MAX(end) AS bis
  FROM t3
  GROUP BY id,num1
  ORDER BY id
非常感谢本文的作者: http://blog.developpez.com/sqlpro/p9821/langage-sql-norme/agregation-d-intervalles-zh-sql-1/     

解决方法

        编辑:这是一个好消息,您的DBA同意升级到较新版本的PostgreSQL。单独的窗口功能使升级成为值得的投资。 正如您所注意到的那样,我的原始答案有一个主要缺陷:每ѭ20one只能有一行。 以下是没有这种限制的更好的解决方案。 我已经使用系统上的测试表(8.4)对其进行了测试。 如果您有时间,我想知道它如何对您的数据执行。 我还在这里写了一个解释:https://www.mechanical-meat.com/1/detail
WITH RECURSIVE t1_rec ( id,\"begin\",\"end\",n ) AS (
    SELECT id,n
      FROM (
        SELECT
            id,CASE 
                WHEN LEAD(\"begin\") OVER (
                PARTITION BY    id
                ORDER BY        \"begin\") <= (\"end\" + interval \'2\' day) 
                THEN 1 ELSE 0 END AS cl,ROW_NUMBER() OVER (
                PARTITION BY    id
                ORDER BY        \"begin\") AS n
        FROM mytable 
    ) s
    WHERE s.cl = 1
  UNION ALL
    SELECT p1.id,p1.\"begin\",p1.\"end\",a.n
      FROM t1_rec a 
           JOIN mytable p1 ON p1.id = a.id
       AND p1.\"begin\" > a.\"begin\"
       AND (a.\"begin\",a.\"end\" + interval \'2\' day) OVERLAPS 
           (p1.\"begin\",p1.\"end\")
)
SELECT t1.id,min(t1.\"begin\"),max(t1.\"end\")
  FROM t1_rec t1
       LEFT JOIN t1_rec t2 ON t1.id = t2.id 
       AND t2.\"end\" = t1.\"end\"
       AND t2.n < t1.n
 WHERE t2.n IS NULL
 GROUP BY t1.id,t1.n
 ORDER BY t1.id,t1.n;
原始(已弃用)答案如下; 注:每
id
限制为一行。 Denis可能会使用
lead()
lag()
是正确的,但还有另一种方法! 您也可以使用所谓的递归SQL解决此问题。 重叠功能也很方便。 我已经在系统上完全测试了此解决方案(8.4)。 它运作良好。
WITH RECURSIVE rec_stmt ( id,begin,end ) AS (
    /* seed statement: 
           start with only first start and end dates for each id 
    */
      SELECT id,MIN(begin),MIN(end)
        FROM mytable seed_stmt
    GROUP BY id

    UNION ALL

    /* iterative (not really recursive) statement: 
           append qualifying rows to resultset 
    */
      SELECT t1.id,t1.begin,t1.end
        FROM rec_stmt r
             JOIN mytable t1 ON t1.id = r.id
         AND t1.begin > r.end
         AND (r.begin,r.end + INTERVAL \'1\' DAY) OVERLAPS 
             (t1.begin - INTERVAL \'1\' DAY,t1.end)
)
  SELECT MIN(begin),MAX(end) 
    FROM rec_stmt
GROUP BY id;
    ,        我并没有完全理解您的问题,但是我绝对确定您需要研究
lead()/lag()
窗口函数。 例如,类似这样的东西将是放置在子查询或公共表表达式中的一个很好的起点,以便识别每个id是否行重叠:
select id,lag(start) over w as prev_start,lag(end) over w as prev_end,start,end,lead(start) over w as next_start,lead(end) over w as next_end
from yourtable
window w as (
       partition by id
       order by start,end
       )
    ,        关于第二个问题,我不确定PostgreSQL,但是在SQL Server中,有一个DATEDIFF(interval,start_date,end_date),可以为您指定两个日期之间的间隔。您可以使用MIN(Begin)作为开始日期,MAX(End)作为结束日期来获取间隔差异。然后,您可能会在case语句中使用它来输出某些内容,尽管您可能需要进行子查询或与您的方案等效的内容。     ,        纯SQL 对于纯SQL解决方案,请查看Adam \的文章并阅读本文(这篇文章是用法语编写的,但是您会发现它不太难阅读)。在咨询了postgresql-mailing-list之后,向我推荐了这篇文章(谢谢!)。 对于我的数据,这是不合适的,因为所有可能的解决方案都需要至少3次自行联接表。对于(非常)大量的数据,这变成了一个问题。 半SQL,半命令式语言 如果您主要关心速度,并且可以使用命令式语言,那么您可以获得更快的速度(当然,这取决于数据量)。就我而言,使用R执行任务(至少)快了1.000倍。 脚步: (1)获取一个.csv文件。照顾排序!!!
COPY (
  SELECT \"ID\",\"BEGIN\",\"END\"
  <sorry,for a reason I don\'t know StackOverflow won\'t let me finish my code here...>
(2)做这样的事情(此代码是R,但是您可以用任何命令式语言做类似的事情):
data - read.csv2(\"</path/to.csv>\")
data$BEGIN - as.Date(data$BEGIN)
data$END - as.Date(data$END)

smoothingEpisodes - function (theData) {

    theLength - nrow(theData)
    if (theLength  2L) return(theData)

    ID - as.integer(theData[[\"ID\"]])
    BEGIN - as.numeric(theData[[\"BEGIN\"]])
    END - as.numeric(theData[[\"END\"]])

    curId - ID[[1L]]
    curBEGIN - BEGIN[[1L]]
    curEND - END[[1L]]



    out.1 - integer(length = theLength)
    out.2 - out.3 - numeric(length = theLength)

    j - 1L

    for(i in 2:nrow(theData)) {
        nextId - ID[[i]]
        nextBEGIN - BEGIN[[i]]
        nextEND - END[[i]]

        if (curId != nextId | (curEND + 1)  nextBEGIN) {
            out.1[[j]] - curId
            out.2[[j]] - curBEGIN
            out.3[[j]] - curEND

            j - j + 1L

            curId - nextId
            curBEGIN - nextBEGIN
            curEND - nextEND
        } else {
            curEND - max(curEND,nextEND,na.rm = TRUE)
        }
    }

    out.1[[j]] - curId
    out.2[[j]] - curBEGIN
    out.3[[j]] - curEND

    theOutput - data.frame(ID = out.1[1:j],BEGIN = as.Date(out.2[1:j],origin = \"1970-01-01\"),END = as.Date(out.3[1:j],origin = \"1970-01-01\"))

    theOutput
}

data1 - smoothingEpisodes(data)

data2 - transform(data1,TAGE = (as.numeric(data1$END - data1$BEGIN) + 1))

write.csv2(data2,file = \"</path/to/output.csv>\")
您可以在此处找到有关此R代码的详细讨论: “平滑”时间数据-可以更高效地完成吗?     

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...