计算sql查询中的治疗天数

时间:2015-03-30 15:53:24

标签: sql oracle

我正在尝试从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月份的部分。为了使问题更复杂,我需要在两年内提供这些月度摘要。所以我的问题是:

  1. 如何包含悬垂疗法,以便仅包括目标时间段内的部分,
  2. 如何在两年内自动生成这些月度摘要?

4 个答案:

答案 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

See SQL Fiddle here.

我正在做的是使用LEVELCONNECT BY根据start_datestop_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