选择不可用的小时数

时间:2016-04-27 20:19:04

标签: oracle plsql plsqldeveloper

我有falowing table machines_hist

Contract      start_time                Fin_time
C1       2016-01-01 05:10:10     2016-01-01 15:10:10
C1       2016-01-02 10:16:20     2016-01-03 12:14:10
C1       2016-01-05 10:16:20     2016-01-10 17:11:10
C1       2016-02-05 02:16:20     2016-01-06 19:18:10

在一份合同上有几台机器。每台机器都有不同的开始时间和结束时间。间隔可能重叠。我必须做一份报告,该报告将显示每月合约中有多少小时的机器无法工作......

有可能吗? 任何消化...

其他信息:该表显示了计算机的停机时间。在一个较大的表中,可能会有一列显示哪个特定机器已关闭,但这与此问题无关;我们需要按合同计算,而不是每台机器。如果有重叠,那么"普通"两台或多台机器之间的停机时间不应重复计算;同时关闭的两台机器与一台机器停机时的机数相同。

2 个答案:

答案 0 :(得分:1)

设置(测试数据):

SQL> select * from machines_hist;

CONTRACT   START_TIME          FIN_TIME
---------- ------------------- -------------------
C1         2015-12-30 05:10:10 2016-01-01 15:10:10
C1         2016-01-02 10:16:20 2016-01-03 12:14:10
C1         2016-01-25 10:16:20 2016-02-10 17:11:10
C1         2016-01-05 02:16:20 2016-01-06 19:18:10
C2         2016-01-15 12:20:22 2016-01-17 13:40:10
C2         2016-02-23 04:13:50 2016-02-24 02:20:44
C3         2016-02-20 10:13:20 2016-02-20 11:16:40
C4         2015-12-23 20:00:00 2015-12-24 12:23:00
C5         2015-12-31 22:34:00 2016-02-23 00:00:00
9 rows selected.
Elapsed: 00:00:00.33

查询 :(注意绑定变量 - 通常由应用程序提供):

with a as (select to_date(:mon || '-' || :yr, 'MON-yyyy') as month_start from dual),
     b as (select add_months(month_start, 1) as month_end from a),
     c as (select contract, greatest(month_start, start_time) as st, 
                            least(month_end, fin_time) as fin
           from machines_hist join a on fin_time >= month_start 
                              join b on start_time <= month_end),
     m as (select contract, st,
                   max(fin) over (partition by contract order by st
                            rows between unbounded preceding and 1 preceding) as m_time
           from c
           union all
           select contract, NULL, max(fin) from c group by contract),
     n as (select contract, st, m_time
           from m
           where st > m_time or st is null or m_time is null),
     f as (select contract, st as st_downtime,
                  lead(m_time) over (partition by contract order by st) as fin_downtime
           from n)
select contract, max(:mon || '-' || :yr) as mth, 
       round(100 * sum(fin_downtime - st_downtime)/
          ((select month_end from b) - (select month_start from a)), 2) as downtime_pct
from f
where st_downtime is not null
group by contract
order by contract
/

绑定变量(说明SQL * Plus接口 - 每个程序都有自己的机制):

SQL> variable yr number
SQL> variable mon varchar2(3)
SQL> begin :mon := 'JAN'; :yr := 2016; end;
  2  /
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.03

输出(注意:脚本保存为“downtime.sql”,通过SQL * Plus调用;本月停机时间百分比表示为22.3表示22.3%等;如果合同没有停机时间,它不包含在输出中)

SQL> start downtime

CONTRACT   MTH          DOWNTIME_PCT
---------- ------------ ------------
C1         JAN-2016            32.24
C2         JAN-2016             6.63
C5         JAN-2016              100
3 rows selected.
Elapsed: 00:00:00.19

答案 1 :(得分:0)

Oracle安装程序

CREATE TABLE machines_hist ( Contract, start_time, Fin_time ) AS
SELECT 'C1', CAST( TIMESTAMP '2016-01-01 05:10:10' AS DATE ), CAST( TIMESTAMP '2016-01-01 15:10:10' AS DATE ) FROM DUAL UNION ALL
SELECT 'C1', CAST( TIMESTAMP '2016-01-02 10:16:20' AS DATE ), CAST( TIMESTAMP '2016-01-03 12:14:10' AS DATE ) FROM DUAL UNION ALL
SELECT 'C1', CAST( TIMESTAMP '2016-01-05 10:16:20' AS DATE ), CAST( TIMESTAMP '2016-01-10 17:11:10' AS DATE ) FROM DUAL UNION ALL
SELECT 'C1', CAST( TIMESTAMP '2016-01-05 02:16:20' AS DATE ), CAST( TIMESTAMP '2016-01-06 19:18:10' AS DATE ) FROM DUAL UNION ALL
SELECT 'C1', CAST( TIMESTAMP '2016-02-01 00:00:00' AS DATE ), CAST( TIMESTAMP '2016-03-01 00:00:00' AS DATE ) FROM DUAL UNION ALL
SELECT 'C1', CAST( TIMESTAMP '2016-04-01 00:00:00' AS DATE ), CAST( TIMESTAMP '2016-04-07 00:00:00' AS DATE ) FROM DUAL UNION ALL
SELECT 'C1', CAST( TIMESTAMP '2016-04-05 00:00:00' AS DATE ), CAST( TIMESTAMP '2016-04-10 00:00:00' AS DATE ) FROM DUAL;

<强>查询

WITH times ( contract, boundary_time, type ) AS (
  SELECT Contract,
         Start_time,
         1
  FROM   machines_hist
  UNION ALL
  SELECT Contract,
         fin_time,
         -1
  FROM   machines_hist
  ORDER BY 1, 2
),
bounds ( contract, boundary_time, type, boundary_value ) AS (
  SELECT t.*,
         SUM( type ) OVER ( PARTITION BY Contract
                            ORDER BY boundary_time ASC, type DESC
                            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
  FROM  times t
),
end_times ( contract, start_time, type, boundary_value, end_time ) AS (
  SELECT b.*,
         LEAD( CASE WHEN type = -1 AND boundary_value = 0 THEN boundary_time END )
           IGNORE NULLS
           OVER ( PARTITION BY Contract ORDER BY boundary_time )
           AS end_time
  FROM   bounds b
),
filtered_end_times ( contract, start_time, end_time ) AS (
  SELECT contract,
         start_time,
         end_time
  FROM   end_times
  WHERE  type = 1
  AND    boundary_value = 1
),
month_boundaries ( start_month, end_month ) AS (
  SELECT TRUNC( MIN( start_time ), 'MM' ),
         CASE WHEN MAX( end_time ) = TRUNC( MAX( end_time ), 'MM' )
              THEN MAX( end_time )
              ELSE TRUNC( MAX( end_time ), 'MM' ) + INTERVAL '1' MONTH
              END
  FROM   filtered_end_times
),
months ( start_month, end_month ) AS (
  SELECT ADD_MONTHS( start_month, LEVEL - 1),
         ADD_MONTHS( start_month, LEVEL)
  FROM   month_boundaries
  CONNECT BY ADD_MONTHS( start_month, LEVEL) <= end_month
)
SELECT contract,
       start_month,
       end_month
         - start_month
         - SUM( LEAST(end_month,end_time)-GREATEST(start_month,start_time) )
           AS days_not_working
FROM   months
       INNER JOIN
       filtered_end_times
       ON ( start_time <= end_month AND end_time >= start_month )
GROUP BY
       contract,
       start_month,
       end_month
ORDER BY
       contract,
       start_month

<强>输出

CONTRACT START_MONTH         DAYS_NOT_WORKING
-------- ------------------- ----------------
C1       2016-01-01 00:00:00       23.8800926 
C1       2016-02-01 00:00:00                0 
C1       2016-03-01 00:00:00               31 
C1       2016-04-01 00:00:00               21