日期范围之间的字段值

时间:2012-06-11 15:28:52

标签: sql postgresql postgresql-9.1

请,有人可以帮助我,如何获取doc状态的信息...... 一般来说,我需要一段时间(开始日期,结束日期过滤器) 检查文件是活动(A)还是非活动(I)

Table Documents
ID  Doc    Date    Status
1   11    1.1.2012.  A
2   11    1.4.2012.  I
3   11    25.4.2012. A
4   11    1.6.2012.  I
5   22    18.4.2012. A
6   22    30.4.2012. I

Dynamic filters: @start,@end

Example: 
@start= 2.3.2012
@end=5.5.2012
Result should be 
11  2.3.-1.4. Status=A 
    1.4.-25.4 Status=I 
    25.4.-5.5. Status=A 
22  2.3.-18.4. 'not exist'
    18.4-30.4. Status=A 
    30.4.-5.5. Status=I 

    If filter is 
@start= 1.2.
@end= 28.2.
Result should be 
11 'A'  
22 'not exist'

If filter is 
@start= 18.4.
@end= 20.4.
Result should be 
11 'I'
22 'A'

修改

对不起,我不想听起来像'为我做'... 我尝试过这样的事情

WITH a AS (
   SELECT documents.*,lag(date) OVER (PARTITION BY doc ORDER BY DATE) AS pre_date
 FROM documents ORDER BY DATE
)
SELECT a.* from a
WHERE (@start between a.pre_date AND a.date) AND (@end between a.pre_date AND a.date)

这不是我需要的。 这也是sql fiddle sqlfiddlelink中的示例。 我更改了Filter表以测试@start和@end

的不同值

由于

2 个答案:

答案 0 :(得分:2)

此查询似乎使用您在sqlfiddle上定义的“过滤器”表生成您要查找的内容。它不包括“不存在”行。我不确定你是否真的想要它,或者你只是想表明它不存在。我假设后者。否则,我猜测过滤表中的一些额外句点需要“联合”。

这个想法是首先创建像你试图用“滞后”做的时期,但是用“引导”来说这个时期的结束是下一个时期的开始。可能想要从领先者中减去1天以使结束日期不包括在内,但我不想更多地卷入其中。

  • 如果没有结束时段,请使用过滤器结束时段(合并)
  • 任何开始日期小于过滤器开始日期都会升级到过滤器开始日期(最大)
  • 任何超过过滤器结束日期的结束日期都将减少到过滤器结束日期(最少)

查询:

SELECT id, doc, status, from_date, to_date
  FROM ( SELECT id, doc, status, GREATEST(d.date, f.start_date) AS from_date
               ,LEAST( COALESCE( lead(date) OVER (PARTITION BY doc ORDER BY date)
                                ,f.end_date
                               )
                       ,f.end_date ) AS to_date
           FROM documents d
               ,filter f
        ) d
  WHERE from_date < to_date
  ORDER BY doc, from_date;

设定:

CREATE TABLE documents(id int, doc int, date date, status varchar (1));

insert into documents values(1, 11, to_date('2012-01-01', 'yyyy-mm-dd'),'A');
insert into documents values(2, 11, to_date('2012-04-01', 'yyyy-mm-dd'),'I');
insert into documents values(3, 11, to_date('2012-04-25', 'yyyy-mm-dd'),'A');
insert into documents values(4, 11, to_date('2012-06-01', 'yyyy-mm-dd'),'I');
insert into documents values(5, 22, to_date('2012-04-18', 'yyyy-mm-dd'),'A');
insert into documents values(6, 22, to_date('2012-04-30', 'yyyy-mm-dd'),'I');

CREATE TABLE filter(start_date date, end_date date);

执行命令

postgres=#     insert into filter values(to_date('2012-02-03', 'yyyy-mm-dd'), to_date('2012-05-05', 'yyyy-mm-dd'));
INSERT 0 1
postgres=#     SELECT id, doc, status, from_date, to_date
postgres-#       FROM ( SELECT id, doc, status, GREATEST(d.date, f.start_date) AS from_date
postgres(#                    ,LEAST( COALESCE( lead(date) OVER (PARTITION BY doc ORDER BY date)
postgres(#                                     ,f.end_date
postgres(#                                    )
postgres(#                            ,f.end_date ) AS to_date
postgres(#                FROM documents d
postgres(#                    ,filter f
postgres(#             ) d
postgres-#       WHERE from_date < to_date
postgres-#       ORDER BY doc, from_date
postgres-# ;
 id | doc | status | from_date  |  to_date
----+-----+--------+------------+------------
  1 |  11 | A      | 2012-02-03 | 2012-04-01
  2 |  11 | I      | 2012-04-01 | 2012-04-25
  3 |  11 | A      | 2012-04-25 | 2012-05-05
  5 |  22 | A      | 2012-04-18 | 2012-04-30
  6 |  22 | I      | 2012-04-30 | 2012-05-05
(5 rows)


postgres=#     truncate table filter;
TRUNCATE TABLE
postgres=#     insert into filter values(to_date('2012-01-02', 'yyyy-mm-dd'), to_date('2012-02-28', 'yyyy-mm-dd'));
INSERT 0 1
postgres=#     SELECT id, doc, status, from_date, to_date
postgres-#       FROM ( SELECT id, doc, status, GREATEST(d.date, f.start_date) AS from_date
postgres(#                    ,LEAST( COALESCE( lead(date) OVER (PARTITION BY doc ORDER BY date)
postgres(#                                     ,f.end_date
postgres(#                                    )
postgres(#                            ,f.end_date ) AS to_date
postgres(#                FROM documents d
postgres(#                    ,filter f
postgres(#             ) d
postgres-#       WHERE from_date < to_date
postgres-#       ORDER BY doc, from_date;
 id | doc | status | from_date  |  to_date
----+-----+--------+------------+------------
  1 |  11 | A      | 2012-01-02 | 2012-02-28
(1 row)


postgres=#     truncate table filter;
TRUNCATE TABLE
postgres=#     insert into filter values(to_date('2012-04-18', 'yyyy-mm-dd'), to_date('2012-04-20', 'yyyy-mm-dd'));
INSERT 0 1
postgres=#     SELECT id, doc, status, from_date, to_date
postgres-#       FROM ( SELECT id, doc, status, GREATEST(d.date, f.start_date) AS from_date
postgres(#                    ,LEAST( COALESCE( lead(date) OVER (PARTITION BY doc ORDER BY date)
postgres(#                                     ,f.end_date
postgres(#                                    )
postgres(#                            ,f.end_date ) AS to_date
postgres(#                FROM documents d
postgres(#                    ,filter f
postgres(#             ) d
postgres-#       WHERE from_date < to_date
postgres-#       ORDER BY doc, from_date;
 id | doc | status | from_date  |  to_date
----+-----+--------+------------+------------
  2 |  11 | I      | 2012-04-18 | 2012-04-20
  5 |  22 | A      | 2012-04-18 | 2012-04-20
(2 rows)


postgres=#

答案 1 :(得分:2)

基本上,@ Glenn的答案涵盖了它。我赞成它。我只发布这个来展示其他细节 - 太多不适合评论:

  • 使用多行INSERT语法。

  • CTE中提供过滤器,这比为此创建额外的表格方便得多。

  • 此查询可以同时处理多个过滤器。

  • 使用lead(date,1,'infinity')来消除对COALESCE的需求。

  • 展示一种不那么复杂的输入日期文字的方式 - ISO 8601格式'yyyy-mm-dd'任何语言区明确无误:

    '2012-02-03'::date
    

    date '2012-02-03'
    

    而不是:

    to_date('2012-02-03', 'yyyy-mm-dd')
    
  • 全部采用噪音较小,格式较为可读的方式

CREATE TEMP TABLE documents (id int, doc int, date date, status "char");

INSERT INTO documents VALUES
 (1,'11','2012-01-01','A')
,(2,'11','2012-04-01','I')
,(3,'11','2012-04-25','A')
,(4,'11','2012-06-01','I')
,(5,'22','2012-04-18','A')
,(6,'22','2012-04-30','I');

WITH filter(filter_id, start_date, end_date) AS( 
    VALUES
     (1, '2012-04-18'::date, '2012-04-20'::date)
    ,(2, '2012-03-02'::date, '2012-05-05'::date)
    )
    , d AS (
    SELECT doc, status, date AS d1
          ,lead(date,1,'infinity') OVER (PARTITION BY doc ORDER BY date) AS d2
    FROM   documents
    )
SELECT f.filter_id, d.doc
      ,GREATEST(f.start_date, d.d1) AS start
      ,LEAST(f.end_date, d.d2) AS end
      ,d.status
FROM   filter f, d
WHERE  f.start_date <= d.d2
AND    f.end_date   >= d.d1
ORDER  BY f.filter_id, d.doc, d.d1;