【问题标题】:Compare multiple date ranges比较多个日期范围
【发布时间】:2012-05-25 15:59:27
【问题描述】:

我正在使用 iReport 3.0.0、PostgreSQL 9.1。对于报告,我需要将发票中的日期范围与过滤器中的日期范围进行比较,如果过滤器范围覆盖部分覆盖等,则打印每个发票代码。复杂化每个发票代码可以有多个日期范围。

表格发票

ID  Code    StartDate   EndDate
1   111     1.5.2012    31.5.2012
2   111     1.7.2012    20.7.2012
3   111     25.7.2012   31.7.2012
4   222     1.4.2012    15.4.2012
5   222     18.4.2012   30.4.2012

示例

过滤器:1.5.2012。 - 5.6.2012.
我需要得到的结果是:

code 111 - partialy covered 
code 222 - invoice missing

过滤器:1.5.2012。 - 2012 年 5 月 31 日。

code 111 - fully covered
code 222 - invoice missing

过滤器:1.6.2012。 - 2012 年 6 月 30 日。

code 111 -  invoice missing
code 222 -  invoice missing

【问题讨论】:

  • 定义“完全覆盖”。过滤器的日期范围必须由一个行覆盖,还是由一个代码的组合行覆盖?

标签: sql postgresql jasper-reports ireport date-range


【解决方案1】:

在评论中澄清后。

我理解的你的任务:

检查所有提供的单个日期范围 (filter) 是否包含在表中代码集的组合日期范围内 (invoice )。

可以使用普通的 SQL 来完成,但它不是一项简单的任务。步骤可能是:

  1. 提供日期范围作为过滤器。

  2. 按代码组合invoice 表中的日期范围。 每个代码可以产生一个或多个范围。

  3. 查找过滤器和合并发票之间的重叠

  4. 分类:完全覆盖/部分覆盖。 可能导致一项全覆盖、一或两个部分覆盖或不覆盖。 减少到最大覆盖范围。

  5. 以合理的排序顺序为(过滤器、代码)的每个组合以及结果覆盖率显示一行

临时过滤器范围

WITH filter(filter_id, startdate, enddate) AS (
    VALUES
      (1, '2012-05-01'::date, '2012-06-05'::date) -- list filters here.
     ,(2, '2012-05-01', '2012-05-31')
     ,(3, '2012-06-01', '2012-06-30')
    )
SELECT * FROM filter;

或者将它们放在一个(临时)表中,然后使用该表。

按代码组合重叠/相邻的日期范围

WITH a AS (
    SELECT code, startdate, enddate
          ,max(enddate) OVER (PARTITION BY code ORDER BY startdate) AS max_end
-- Calculate the cumulative maximum end of the ranges sorted by start
    FROM   invoice
    ), b AS (
    SELECT *
          ,CASE WHEN lag(max_end) OVER (PARTITION BY code
                                        ORDER BY startdate) + 2 > startdate
-- Compare to the cumulative maximum end of the last row.
-- Only if there is a gap, start a new group. Therefore the + 2.
           THEN 0 ELSE 1 END AS step
    FROM   a
    ), c AS (
    SELECT code, startdate, enddate, max_end
          ,sum(step) OVER (PARTITION BY code ORDER BY startdate) AS grp
-- Members of the same date range end up in the same grp
-- If there is a gap, the grp number is incremented one step
    FROM   b
    )
SELECT code, grp
      ,min(startdate) AS startdate
      ,max(enddate) AS enddate
FROM   c
GROUP  BY 1, 2
ORDER  BY 1, 2

另一种最终选择(可能更快,也可能更快,您必须进行测试):

SELECT DISTINCT code, grp
          ,first_value(startdate) OVER w AS startdate
          ,last_value(enddate) OVER w AS enddate
FROM   c
WINDOW W AS (PARTITION BY code, grp ORDER BY startdate
             RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
ORDER  BY 1, 2;

合并到一个查询

WITH 
    -- supply one or more filter values
    filter(filter_id, startdate, enddate) AS (
    VALUES
      (1, '2012-05-01'::date, '2012-06-05'::date) -- cast values in first row
     ,(2, '2012-05-01', '2012-05-31')
     ,(3, '2012-06-01', '2012-06-30')
    )
    -- combine date ranges per code
    ,a AS (
    SELECT code, startdate, enddate
          ,max(enddate) OVER (PARTITION BY code ORDER BY startdate) AS max_end
    FROM   invoice
    ), b AS (
    SELECT *
          ,CASE WHEN (lag(max_end) OVER (PARTITION BY code ORDER BY startdate)
                      + 2) > startdate THEN 0 ELSE 1 END AS step
    FROM   a
    ), c AS (
    SELECT code, startdate, enddate, max_end
          ,sum(step) OVER (PARTITION BY code ORDER BY startdate) AS grp
    FROM   b
    ), i AS ( -- substitutes original invoice table
    SELECT code, grp
          ,min(startdate) AS startdate
          ,max(enddate) AS enddate
    FROM   c
    GROUP  BY 1, 2
    )
    -- match filters
    , x AS (
    SELECT f.filter_id, i.code
            ,bool_or(f.startdate >= i.startdate
              AND f.enddate   <= i.enddate) AS full_cover
    FROM   filter f
    JOIN   i ON i.enddate >= f.startdate
            AND i.startdate <= f.enddate -- only overlapping
    GROUP  BY 1,2
    )
SELECT f.*, i.code
      ,CASE x.full_cover
        WHEN TRUE  THEN 'fully covered'
        WHEN FALSE THEN 'partially covered'
        ELSE            'invoice missing'
       END AS covered
FROM   (SELECT DISTINCT code FROM i) i
CROSS  JOIN filter f -- all combinations of filter and code
LEFT   JOIN x USING (filter_id, code)    -- join in overlapping
ORDER  BY filter_id, code;

在 PostgreSQL 9.1 上测试并为我工作。

【讨论】:

  • 谢谢你的回答,我会试试的。过滤器的日期范围必须由一个代码的组合行覆盖。如何连接发票的日期范围?再次感谢!
  • 我添加了一个查询来合并重叠范围。
  • 非常感谢!巧妙!我用 Sql fiddle 试过了,它工作正常!如果有人想看,这里是链接sqlfiddle.com/#!1/ab70b/1 还没有看懂每一步,但很有帮助!谢谢!
  • @lana80:我使用了广泛的技术来保持它尽可能简单。这是我上周的杰作。 ;)
猜你喜欢
  • 2010-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多