比较多个日期范围 [英] Compare multiple date ranges

查看:117
本文介绍了比较多个日期范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using iReport 3.0.0, PostgreSQL 9.1. For a report I need to compare date ranges from invoices with date ranges in filters and print for every invoice code if a filter range is covered, partially covered, etc. To complicate things, there can be multiple date ranges per invoice code.

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。

我需要得到的结果是:

Examples

Filter: 1.5.2012. - 5.6.2012.
Result that I need to get is:

code 111 - partialy covered 
code 222 - invoice missing

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

Filter: 1.5.2012. - 31.5.2012.

code 111 - fully covered
code 222 - invoice missing

过滤:1.6.2012。 - 30.6.2012。

Filter: 1.6.2012. - 30.6.2012.

code 111 -  invoice missing
code 222 -  invoice missing


推荐答案

在评论中澄清。

检查所有提供的个人日期范围(过滤器)表中是否包含组合日期范围的代码集( invoice )。

Check for all supplied individual date ranges (filter) whether they are are covered by the combined date ranges of sets of codes in your table (invoice).

可以使用纯SQL完成,但不是一项简单的任务。步骤可以是:

It can be done with plain SQL, but it is not a trivial task. The steps could be:


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

  1. Supply date ranges as filters.

将每个代码的 invoice 表中的日期范围组合在一起。
每个代码可以生成一个或多个范围。

Combine date ranges in invoice table per code. Can result in one or more ranges per code.

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

Look for overlaps between filters and combined invoices

分类:完全覆盖/部分覆盖。
可以产生一次全覆盖,一次或两次部分覆盖或无覆盖。
降低到最高覆盖水平。

Classify: fully covered / partially covered. Can result in one full coverage, one or two partial coverages or no coverage. Reduce to maximum level of coverage.

显示每行(过滤器,代码)组合的一行以及最终的覆盖范围订单

Display one row for every combination of (filter, code) with the resulting coverage, in a sensible sort order



特设过滤器范围



Ad hoc filter ranges

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;

将它们放入(临时)表中并使用表格代替。

Or put them in a (temporary) table and use the table instead.

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(可能更快或不更快,你必须测试):

Alternative final SELECT (may be faster or not, you'll have to test):

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;



合并到一个查询



Combine to one query

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上测试并适用于我。

Tested and works for me on PostgreSQL 9.1.

这篇关于比较多个日期范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆