一段时间内的天数

时间:2015-05-20 17:48:03

标签: sql oracle

我有包含开始日期和结束日期的项目列表。项目属于用户。对于一个项目,期限可以为1 - 5年。我想找到我将从查询传递的给定日期范围之间的天数。期间始终为sysdate,结束sysdate - 5 years

返回的天数也必须在期间范围内。

示例:

我从15.05.2015开始查询)因为我是用户,所以我需要找到所有天在2010年5月15日至2015年5月15日之间

在此期间,有2件物品属于我:

项目1)2010年1月1日 - 2010年12月31日。有效范围:15.05.2010 - 2010年12月31日= ~195天

项目2)2015年1月1日 - 2015年12月31日。有效范围:01.01.2015 - 2015年5月15日= ~170天

我需要这些日子的总和恰好在那个时期。

对于现在的查询,我只需要计算一个项目的全部范围(简化):

SELECT SUM(i.end_date - i.start_date) AS total_days 
FROM items i 
WHERE i.start_date >= to_date('2010-15-05', 'yyyy-mm-dd')
AND i.end_date <= to_date('2015-15-05', 'yyyy-mm-dd')
AND i.user = 'me'

所以现在这会给我一些2年期的日期,这是错误的,我应该如何更新我的选择金额以包括期间的日期?正确的结果将是195 + 170.目前我会得到365 + 365或其他东西。

3 个答案:

答案 0 :(得分:1)

假设时间段没有重叠:

SELECT SUM(LEAST(i.end_date, DATE '2015-05-15') -
           GREATEST(i.start_date, DATE '2010-05-15')
          ) AS total_days 
FROM items i 
WHERE i.start_date >= DATE '2010-05-15' AND
      i.end_date <= DATE '2015-05-15' AND
      i.user = 'me';

答案 1 :(得分:1)

使用案例陈述根据案例评估设定开始日期和结束日期的日期。

Select SUM(
  (case when i.end_date > to_date('2015-15-05','yyyy-mm-dd') then 
   to_date('2015-15-05','yyyy-mm-dd') else 
   i.end_date end) - 
  (case when i.start_date< to_date('2010-15-05','yyyy-mm-dd') then 
    to_date('2010-15-05','yyyy-mm-dd') else 
    i.end_date end)) as total_days
FROM items i 
WHERE i.start_date >= to_date('2010-15-05', 'yyyy-mm-dd')
AND i.end_date <= to_date('2015-15-05', 'yyyy-mm-dd')
AND i.user = 'me'

答案 2 :(得分:1)

  

期间始终为sysdate,结束sysdate - 5 years

您可以使用:SYSDATESYSDATE - INTERVAL '5' YEAR

  

项目1)2010年1月1日 - 2010年12月31日。有效范围:15.05.2010 - 2010年12月31日   = ~195天

     

项目2)2015年1月1日 - 2015年12月31日。有效范围:01.01.2015 - 2015年5月15日   = ~170天

假设这些示例显示start_date - end_date,有效范围是您对该特定SYSDATE的预期答案,那么您可以使用:

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE items ( "user", start_date, end_date ) AS
          SELECT 'me', DATE '2010-01-01', DATE '2010-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2015-01-01', DATE '2015-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2009-01-01', DATE '2009-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2009-01-01', DATE '2016-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2012-01-01', DATE '2012-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2013-01-01', DATE '2013-01-01' FROM DUAL;

查询1

SELECT "user",
       TO_CHAR( start_date, 'YYYY-MM-DD' ) AS start_date,
       TO_CHAR( end_date, 'YYYY-MM-DD' ) AS end_date,
       TO_CHAR( GREATEST(TRUNC(i.start_date), TRUNC(SYSDATE)-INTERVAL '5' YEAR), 'YYYY-MM-DD' ) AS valid_start,
       TO_CHAR( LEAST(TRUNC(i.end_date),TRUNC(SYSDATE)), 'YYYY-MM-DD' ) AS valid_end,
       LEAST(TRUNC(i.end_date),TRUNC(SYSDATE))
         - GREATEST(TRUNC(i.start_date), TRUNC(SYSDATE)-INTERVAL '5' YEAR)
         + 1
         AS total_days 
FROM   items i
WHERE  i."user" = 'me'
AND    TRUNC(i.start_date) <= TRUNC(SYSDATE)
AND    TRUNC(i.end_date)   >= TRUNC(SYSDATE) - INTERVAL '5' YEAR

<强> Results

| user | START_DATE |   END_DATE | VALID_START |  VALID_END | TOTAL_DAYS |
|------|------------|------------|-------------|------------|------------|
|   me | 2010-01-01 | 2010-12-31 |  2010-05-21 | 2010-12-31 |        225 |
|   me | 2015-01-01 | 2015-12-31 |  2015-01-01 | 2015-05-21 |        141 |
|   me | 2009-01-01 | 2016-12-31 |  2010-05-21 | 2015-05-21 |       1827 |
|   me | 2012-01-01 | 2012-12-31 |  2012-01-01 | 2012-12-31 |        366 |
|   me | 2013-01-01 | 2013-01-01 |  2013-01-01 | 2013-01-01 |          1 |

这假设开始日期是在一天的开始(00:00),结束日期是在一天结束时(24:00) - 所以,如果开始和结束日期是相同的,那么您希望结果为1天(即00:00 - 24:00)。相反,如果您希望结果为0,则从总天数值的计算中删除+1

查询2

如果您想要所有这些有效范围的总和,并且乐于多次计算重叠范围内的日期,那么只需将其包装在SUM聚合函数中:

SELECT SUM( LEAST(TRUNC(i.end_date),TRUNC(SYSDATE))
         - GREATEST(TRUNC(i.start_date), TRUNC(SYSDATE)-INTERVAL '5' YEAR)
         + 1 )
         AS total_days 
FROM   items i
WHERE  i."user" = 'me'
AND    TRUNC(i.start_date) <= TRUNC(SYSDATE)
AND    TRUNC(i.end_date)   >= TRUNC(SYSDATE) - INTERVAL '5' YEAR

<强> Results

| TOTAL_DAYS |
|------------|
|       2560 |

查询3

现在,如果您想获得该范围内所有有效天数的计数,而不是多次计算范围内的重叠,那么您可以这样做:

WITH ALL_DATES_IN_RANGE AS (
  SELECT TRUNC(SYSDATE) - LEVEL + 1 AS valid_date
  FROM   DUAL
  CONNECT BY LEVEL <= SYSDATE - (SYSDATE - INTERVAL '5' YEAR) + 1
)
SELECT COUNT(1) AS TOTAL_DAYS
FROM   ALL_DATES_IN_RANGE a
WHERE  EXISTS ( SELECT 'X'
                FROM   items i
                WHERE  a.valid_date BETWEEN i.start_date AND i.end_date
                AND    i."user" = 'me' )

<强> Results

| TOTAL_DAYS |
|------------|
|       1827 |