我正在尝试从oracle数据库计算每月治疗天数。 (大大简化的)数据如下:
Therapies
+-----------+-----------+----------+
| Rx Number | StartDate | StopDate |
|-----------+-----------+----------|
| 1 | 12-29-14 | 1-10-15 |
| 2 | 1-2-15 | 1-14-15 |
| 3 | 1-29-15 | 2-15-15 |
+-----------+-----------+----------+
出于本示例的目的,假设所有时间都是午夜。该表中治疗的总天数为(10-1 + 32-29)+(14-2)+(15-1 + 32-29)= 41.该表中1月治疗的总天数为(10) -1)+(14-2)+(32-29)= 24。
如果我想计算一月份的治疗天数,我最大的努力是以下查询:
SELECT SUM(stopdate - startdate)
FROM therapies
WHERE startdate > to_date('01-JAN-15')
AND stopdate < to_date ('01-FEB-15');
然而,根本没有捕获rx的1和3。我可以尝试以下方法:
SELECT SUM(stopdate - startdate)
FROM therapies
WHERE stopdate > to_date('01-JAN-15')
AND startdate < to_date ('01-FEB-15');
但这将包括第一次和第三次治疗的整个持续时间,而不仅仅是1月份的部分。为了使问题更复杂,我需要在两年内提供这些月度摘要。所以我的问题是:
答案 0 :(得分:2)
我如何包括悬垂疗法,只包括部分 在目标时间段内包括?
select sum(
greatest(least(stopdate, date '2015-01-31' + 1)
- greatest(startdate, date '2015-01-01'), 0)) suma
from therapies
如何通过两个自动生成这些月度摘要 年度?
with period as (select date '2014-01-01' d1, date '2015-12-31' d2 from dual),
months as (select trunc(add_months(d1, level-1), 'Month') dt
from period connect by add_months(d1, level-1)<d2)
select to_char(dt, 'yyyy-mm') mth,
sum(greatest(least(stopdate, add_months(dt, 1)) - greatest(startdate, dt), 0)) suma
from therapies, months
group by to_char(dt, 'yyyy-mm') order by mth
以上查询产生了所需的输出。请在适当的地方插入您的日期以更改分析的期间。
在第二个SQL内部子查询months
给出24个日期,每个月一个。其余的只是机动
功能greatest()
,least()
和一些数学。
答案 1 :(得分:1)
使用case语句设置开始日期和停止日期。如下所示:
select sum(
Stopdate -
(case Startdate
when startdate < to_date(@YourBeginingDate) then To_date(@YourBeginingDate)
else startdate
end)
FROM therapies
WHERE stopdate > to_date(@YourBeginingDate)
AND StartDate < to_date(@YourEndingDate)
答案 2 :(得分:1)
我会做以下事情:
WITH t1 AS (
SELECT 1 AS rx, DATE'2014-12-29' AS start_date
, DATE'2015-01-10' AS stop_date
FROM dual
UNION ALL
SELECT 2, DATE'2015-01-02', DATE'2015-01-14'
FROM dual
UNION ALL
SELECT 3, DATE'2015-01-29', DATE'2015-02-15'
FROM dual
)
SELECT TRUNC(rx_dt, 'MONTH') AS rx_month, SUM(rx_cnt) AS rx_day_cnt
FROM (
SELECT rx_dt, COUNT(*) AS rx_cnt
FROM (
SELECT rx, start_date + LEVEL - 1 AS rx_dt
FROM t1
CONNECT BY start_date + LEVEL - 1 < stop_date
AND PRIOR rx = rx
AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
) GROUP BY rx_dt
) GROUP BY TRUNC(rx_dt, 'MONTH')
ORDER BY rx_month
结果:
12/1/2014 12:00:00 AM 2
1/1/2015 12:00:00 AM 24
2/1/2015 12:00:00 AM 15
我正在做的是使用LEVEL
和CONNECT BY
根据start_date
和stop_date
(不包括在内)获取治疗的所有日子。然后我GROUP BY
治疗日期(rx_dt
)来处理重叠疗法。然后使用GROUP BY
函数TRUNC()
治疗月。
这应该可以在两年(或更长)期间正常工作;只需在最后GROUP BY
之前添加该过滤器:
WHERE rx_dt >= DATE'2014-01-01'
AND rx_dt < DATE'2016-01-01'
GROUP BY TRUNC(rx_dt, 'MONTH')
请注意,如果您的主键是复合键,则应在CONNECT BY
子句中包含所有列:
CONNECT BY start_date + LEVEL - 1 < stop_date
AND PRIOR rx = rx
AND PRIOR patient_id = patient_id
--etc.
答案 3 :(得分:0)
这有点棘手,因为您需要从以下会话中捕获日期:
要获取这些会话,可以使用这样的WHERE语句(@符号表示这些是传入的变量):
*示例在TSQL中,PLSQL可能有一些不同的语法
WHERE startdate < @endDate AND stopdate > @startDate
这应该捕获我列出的所有四种情景。
然后你只需要捕捉月内发生的日子。我使用一个查询来执行此操作,如果超出范围,则将startdate / enddate替换为日期范围限制,如下所示:
SELECT
CASE WHEN enddate > @endDate then @endDate ELSE enddate END -
CASE WHEN startdate < @startDate THEN @startDate ELSE startdate END
所以你的整个查询应该是这样的:
SELECT
SUM(
CASE WHEN enddate > @endDate then @endDate ELSE enddate END -
CASE WHEN startdate < @startDate THEN @startDate ELSE startdate END
)
FROM therapies
WHERE startdate < @endDate AND stopdate > @startDate
如果你想运行它两年,那么将该代码扔到一个接受@startDate和@endDate参数的函数中,然后从一个给你两年数月的查询中调用它,如下所示:
WITH dateCTE AS (
SELECT
GETDATE() AS StartDate,
DATEADD(Month, 1, GETDATE()) AS EndDate
UNION ALL
SELECT
DATEADD(MONTH, -1, StartDate),
DATEADD(MONTH, -1, EndDate)
FROM dateCTE
WHERE StartDate > DATEADD(YEAR, -2, GETDATE())
)
SELECT
StartDate,
EndDate,
SomeFunction(StartDate, EndDate)
FROM dateCTE