具有特定状态的不同日子-SQL Oracle

时间:2018-08-09 16:07:37

标签: sql oracle

我有一个这样的表(租):

user_rent| book_rent | rent_from  | rent_to
-------------------------------------------
John Doe | Hobbit    | 01.04.2018 | 18.05.2018
Jane Doe | Inferno   | 12.07.2018 | 25.07.2018
Jane Doe | Hamlet    | 12.07.2018 | 05.08.2018
Sara Doe | Macbeth   | 01.06.2018 | 08.06.2018
Sara Doe | Othello   | 08.06.2018 | 15.06.2018

我需要的是每个用户在一定时期内以其名义租借的图书的天数。有争议的期间是01.05.2018-31.07.2018

这里有三个不同的问题。

  1. 这本书可以租借,也可以稍后再归还,但我只需要在此时间范围内计算天数(即John Doe在该时间范围内只有18天-01.05.2018-18.05.2018)
  2. 开始日期和结束日期都需要输入总和(例如,01.06.2018-03.06.2018是三天,而不是两天)
    如果一个人立即租借新书(例如Sara Doe应该有15天的1-8和8-15天),这将成为一个问题。
  3. 某些用户可以有两本书,但两天重叠,但我只需要不同的日子(即Jane Doe应该有20天-2018年12月12日-2018年7月31日

我的结果表应如下所示:

user_rent| days
-----------------
John Doe | 18
Jane Doe | 20
Sara Doe | 15

但是我听不懂:(
我将不胜感激。

编辑: 这是我的尝试,只能解决第一个问题...

SELECT USER_RENT, SUM(DIFF) DAYS
FROM
(
  SELECT DISTINCT(USER_RENT), REAL_START, REAL_END, REAL_END-REAL_START+1 DIFF
  FROM
  (
    SELECT DISTINCT(USER_RENT), 
           CASE WHEN RENT_FROM < '01.05.2018' 
                THEN TO_DATE(LAST_DAY(ADD_MONTHS(SYSDATE, -4))+1) 
                ELSE RENT_FROM 
                END REAL_START,
           CASE WHEN RENT_TO > '31.07.2018' 
                THEN TO_DATE(LAST_DAY(ADD_MONTHS(SYSDATE, -1))) 
                ELSE RENT_TO 
                END REAL_END
    FROM RENT
    WHERE RENT_TO > '30.04.2018'
    AND RENT_FROM < '01.08.2018'
  )
)
GROUP BY USER_RENT
;

1 个答案:

答案 0 :(得分:6)

您可以使用分层查询将日期范围扩展到所有日期:

select date '2018-05-01' + level - 1
from dual
connect by level <= date '2018-07-31' - date '2018-05-01' + 1

然后可以将其用作内联视图或CTE,并根据这些日期加入到rent表中:

with days (day) as (
  select date '2018-05-01' + level - 1
  from dual
  connect by level <= date '2018-07-31' - date '2018-05-01' + 1
)
select r.user_name, count(distinct d.day)
from days d
join rent r
on r.rent_from <= d.day
and r.rent_to >= d.day
group by r.user_name;

或者从12c开始,您可以使用cross apply做同样的事情:

select r.user_name, count(distinct d.day) as days
from rent r
cross apply (
  select date '2018-05-01' + level - 1 as day
  from dual
  connect by level <= date '2018-07-31' - date '2018-05-01' + 1
) d
where r.rent_from <= d.day
and r.rent_to >= d.day
group by r.user_name
order by r.user_name;

将示例数据与另一个CTE一起进行演示:

with rent (user_name, book, rent_from, rent_to) as (
            select 'John Doe', 'Hobbit',  date '2018-04-01', date '2018-05-18' from dual
  union all select 'Jane Doe', 'Inferno', date '2018-07-12', date '2018-07-25' from dual
  union all select 'Jane Doe', 'Hamlet',  date '2018-07-12', date '2018-08-05' from dual
  union all select 'Sara Doe', 'Macbeth', date '2018-06-01', date '2018-06-08' from dual
  union all select 'Sara Doe', 'Othello', date '2018-06-08', date '2018-06-15' from dual
),
days (day) as (
  select date '2018-05-01' + level - 1
  from dual
  connect by level <= date '2018-07-31' - date '2018-05-01' + 1
)
select r.user_name, count(distinct d.day) as days
from days d
join rent r
on r.rent_from <= d.day
and r.rent_to >= d.day
group by r.user_name
order by r.user_name;

USER_NAME       DAYS
--------- ----------
Jane Doe          20
John Doe          18
Sara Doe          15

我已将示例中的列名称更改为合法值。