全部在10分钟内选择记录

时间:2015-08-09 06:16:48

标签: sql oracle plsql oracle10g

我的Oracle数据库中有一些数据源。

如果某个特定的Office_ID已被停用并且它具有特定日期的所有三个客户端(A,B,C),那么我们必须检查所有客户端是否已经消失。如果是,那么我们需要检查所有客户的时间范围是否在10分钟之内。

如果某一办公室每天重复三次,我们宣布办公室已关闭。

以下是一些示例数据:

+-----------+-----------+--------------+--------+
| OFFICE_ID | FAIL_TIME | ACTIVITY_DAY | CLIENT |
| 1002      | 5:39:00   | 23/01/2015   | A      |
| 1002      | 17:49:00  | 23/12/2014   | A      |
| 1002      | 18:41:57  | 1/5/2014     | B      |
| 1002      | 10:32:00  | 1/7/2014     | A      |
| 1002      | 10:34:23  | 1/7/2014     | B      |
| 1002      | 10:35:03  | 1/7/2014     | C      |
| 1002      | 12:08:52  | 1/7/2014     | B      |
| 1002      | 12:09:00  | 1/7/2014     | A      |
| 1002      | 12:26:10  | 1/7/2014     | B      |
| 1002      | 13:31:32  | 1/7/2014     | B      |
| 1002      | 15:24:06  | 1/7/2014     | B      |
| 1002      | 15:55:06  | 1/7/2014     | C      |
+-----------+-----------+--------------+--------+

结果应该是这样的:

1002    10:32:00      A
1002    10:34:23      B
1002    10:35:03      C

任何帮助将不胜感激。我正在寻找SQL查询或PL / SQL过程。

3 个答案:

答案 0 :(得分:1)

使用COUNT analytic functionRANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING避免自连接的解决方案:

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE Test ( OFFICE_ID, FAIL_TIME, ACTIVITY_DAY, CLIENT ) AS
          SELECT 1002,  '5:39:00', '23/01/2015', 'A' FROM DUAL
UNION ALL SELECT 1002, '17:49:00', '23/12/2014', 'A' FROM DUAL
UNION ALL SELECT 1002, '18:41:57', '1/5/2014', 'B' FROM DUAL
UNION ALL SELECT 1002, '10:32:00', '1/7/2014', 'A' FROM DUAL
UNION ALL SELECT 1002, '10:34:23', '1/7/2014', 'B' FROM DUAL
UNION ALL SELECT 1002, '10:35:03', '1/7/2014', 'C' FROM DUAL
UNION ALL SELECT 1002, '12:08:52', '1/7/2014', 'B' FROM DUAL
UNION ALL SELECT 1002, '12:09:00', '1/7/2014', 'A' FROM DUAL
UNION ALL SELECT 1002, '12:26:10', '1/7/2014', 'B' FROM DUAL
UNION ALL SELECT 1002, '13:31:32', '1/7/2014', 'B' FROM DUAL
UNION ALL SELECT 1002, '15:24:06', '1/7/2014', 'B' FROM DUAL
UNION ALL SELECT 1002, '15:55:06', '1/7/2014', 'C' FROM DUAL

查询1

WITH Times AS (
  SELECT OFFICE_ID,
         TO_DATE( ACTIVITY_DAY || ' ' || FAIL_TIME, 'DD/MM/YYYY HH24/MI/SS' ) AS FAIL_DATETIME,
         CLIENT
  FROM   Test
),
Next_Times As (
  SELECT OFFICE_ID,
         FAIL_DATETIME,
         COUNT( CASE CLIENT WHEN 'A' THEN 1 END ) OVER ( PARTITION BY OFFICE_ID ORDER BY FAIL_DATETIME RANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING ) AS COUNT_A,
         COUNT( CASE CLIENT WHEN 'B' THEN 1 END ) OVER ( PARTITION BY OFFICE_ID ORDER BY FAIL_DATETIME RANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING ) AS COUNT_B,
         COUNT( CASE CLIENT WHEN 'C' THEN 1 END ) OVER ( PARTITION BY OFFICE_ID ORDER BY FAIL_DATETIME RANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING ) AS COUNT_C
  FROM   Times
)
SELECT OFFICE_ID,
       TO_CHAR( FAIL_DATETIME, 'HH24:MI:SS' ) AS FAIL_TIME,
       TO_CHAR( FAIL_DATETIME, 'DD/MM/YYYY' ) AS ACTIVITY_DAY       
FROM   Next_Times
WHERE  COUNT_A > 0
AND    COUNT_B > 0
AND    COUNT_C > 0
ORDER BY FAIL_DATETIME

<强> Results

| OFFICE_ID | FAIL_TIME | ACTIVITY_DAY |
|-----------|-----------|--------------|
|      1002 |  10:32:00 |   01/07/2014 |
|      1002 |  10:34:23 |   01/07/2014 |
|      1002 |  10:35:03 |   01/07/2014 |

答案 1 :(得分:0)

要识别您可以连接表格的记录三次,如下所示:

SELECT
  a.*, b.*, c.*
FROM FailLog a INNER JOIN
     FailLog b ON b.OFFICE_ID = A.OFFICE_ID AND 
          a.CLIENT = 'A' AND 
          b.CLIENT = 'B' AND
          b.ACTIVITY_DAY = a.ACTIVITY_DAY INNER JOIN
     FailLog c ON c.OFFICE_ID = A.OFFICE_ID AND 
          c.CLIENT = 'C' AND 
          c.ACTIVITY_DAY = a.ACTIVITY_DAY AND
          -- need to calculate difference in min here
          GREATEST (a.FAIL_TIME, b. FAIL_TIME, c. FAIL_TIME) - 
          LEAST (a.FAIL_TIME, b. FAIL_TIME, c. FAIL_TIME) <= 10 

输出将为您提供一行而不是问题中请求的三行,但这将是故障数据的正确级别,因为所有三个客户端都应该拥有它。

答案 2 :(得分:0)

我们需要的第一件事就是比较FAIL_TIME。由于您还没有发布表结构,我们假设我们正在处理字符串。

Oracle有一些用于投射日期和字符串的简洁内置插件。如果我们连接ACTIVITY_DATE和FAIL_TIME,我们可以将它们转换为DATE数据类型:

to_date(ACTIVITY_DAY||' '||FAIL_TIME, 'dd/mm/yyyy hh24:mi:ss')

我们可以将其转换为表示午夜过后秒数的字符串:

to_char(to_date(ACTIVITY_DAY||' '||FAIL_TIME, 'dd/mm/yyyy hh24:mi:ss'), 'sssss')

然后我们可以将 转换为数字,我们可以在某些算术中使用它来与其他行进行比较;十分钟= 600秒。

接下来我们可以使用子查询分解(WITH子句)。这种语法的一个优点是我们可以将一个子查询的输出传递给另一个子查询,因此我们只需要编写一次这种粗糙的嵌套转换表达式。

with t as
    ( select OFFICE_ID
               , ACTIVITY_DAY
               , FAIL_TIME
               , to_number(to_char(to_date(ACTIVITY_DAY||' '||FAIL_TIME, 'dd/mm/yyyy hh24:mi:ss'), 'sssss')) FAIL_TIME_SSSSS
               , CLIENT
      from faillog
    )

我们可以使用这个子查询来构建其他子查询,这些子查询将表的行分成每个CLIENT的集合,以便在我们的主查询中使用。

最后,我们可以使用分析COUNT()函数来跟踪每个OFFICE和ACTIVITY_DATE组合中有多少个FAIL_TIME。

count(*) over (partition by a.OFFICE_ID, a.ACTIVITY_DAY) 

将所有内容放在一个内联视图中,我们可以测试我们是否可以“宣布办公室已关闭”。

select * from (
    with t as ( select OFFICE_ID
                       , ACTIVITY_DAY
                       , FAIL_TIME
                       , to_number(to_char(to_date(ACTIVITY_DAY||' '||FAIL_TIME, 'dd/mm/yyyy hh24:mi:ss'), 'sssss')) FAIL_TIME_SSSSS
                       , CLIENT
                from faillog
                )
         , a as (select *
                  from t
                  where CLIENT = 'A' )
         , b as (select *
                  from t
                  where CLIENT = 'B' )
         , c as (select *
                  from t
                  where CLIENT = 'C' )
    select a.OFFICE_ID
           , a.ACTIVITY_DAY 
           , a.FAIL_TIME as a_fail_time
           , b.FAIL_TIME as b_fail_time
           , c.FAIL_TIME as a_fail_time
           , count(*) over (partition by a.OFFICE_ID, a.ACTIVITY_DAY) as fail_count
    from a 
         join b on a.OFFICE_ID = b.OFFICE_ID and a.ACTIVITY_DAY = b.ACTIVITY_DAY
         join c on a.OFFICE_ID = c.OFFICE_ID and a.ACTIVITY_DAY = c.ACTIVITY_DAY
    where a.FAIL_TIME_SSSSS between b.FAIL_TIME_SSSSS - 600 and b.FAIL_TIME_SSSSS + 600
    and   a.FAIL_TIME_SSSSS between c.FAIL_TIME_SSSSS - 600 and c.FAIL_TIME_SSSSS + 600
    and   b.FAIL_TIME_SSSSS between a.FAIL_TIME_SSSSS - 600 and a.FAIL_TIME_SSSSS + 600
    and   b.FAIL_TIME_SSSSS between c.FAIL_TIME_SSSSS - 600 and c.FAIL_TIME_SSSSS + 600
    and   c.FAIL_TIME_SSSSS between a.FAIL_TIME_SSSSS - 600 and a.FAIL_TIME_SSSSS + 600
    and   c.FAIL_TIME_SSSSS between b.FAIL_TIME_SSSSS - 600 and b.FAIL_TIME_SSSSS + 600
)
where fail_count >= 3
/

备注

  1. 显然我在子查询中硬编码了CLIENT标识符。 可以避免硬编码,但样本查询已经足够复杂了。
  2. 此查询不会搜索 三胞胎。假设A,B和C各有一个失败 在一个十分钟的窗口内,无论有多少个实例 每个CLIENT都出现在窗口中。你的一切都没有 业务规则说这是错误的。
  3. 同样,同一个实例 一个CLIENT可以与其他CLIENT的实例匹配 重叠的窗户。现在这可能是不受欢迎的:双倍或三倍 计数可能会使FAIL_COUNT膨胀。但同样,处理这个将 使最终查询更复杂。
  4. 所呈现的查询对于A,B和C FAIL_TIME值的每个不同组合具有一行。如果您确实需要为每个CLIENT / FAIL_TIME添加一行,则可以旋转结果集。