【问题标题】:field value between date range日期范围之间的字段值
【发布时间】:2012-06-11 15:28:52
【问题描述】:

请,有人可以帮助我,如何获取文档状态的信息... 一般来说,我需要给定的时间段(开始日期,结束日期过滤器) 检查文档是活动 (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 中的示例。 我更改过滤表以测试 @start 和 @end 的不同值

谢谢

【问题讨论】:

  • 这不是问题。这是一个“为我做”的请求。请发布您尝试过的内容以及无效的内容。

标签: sql postgresql postgresql-9.1


【解决方案1】:

基本上,@Glenn 的回答涵盖了它。我投了赞成票。我发布此内容只是为了展示更多细节 - 太多无法放入评论:

  • 使用多行 INSERT 语法。

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

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

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

  • 演示一种不那么复杂的日期文字输入方式 - ISO 8601 格式 'yyyy-mm-dd'any 语言环境中是明确的:

    '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;

【讨论】:

  • 不错。很有用。感谢您花时间指出这一点。我知道我会为自己未来的编码记住这些想法。干杯。
【解决方案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=#

【讨论】:

  • 这很有帮助!感谢您的时间!最好的问候!
猜你喜欢
  • 2021-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-15
  • 1970-01-01
  • 2016-10-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多