2015-08-09 43 views
2

我在Oracle数据库中有一些数据来源。在10分钟内选择所有记录

如果一个特定的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过程。

+0

什么FAIL_TIME的数据类型? – APC

回答

1

使用COUNTanalytic functionRANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING避免自连接A液:

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 | 
+0

OP在问题中指定10g。但是,在[10g]中支持 PRECEDING语法的范围(http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions001.htm#i97640),所以这是更优雅的解决方案。 – APC

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 

输出会给你一个排,而不是三个作为问题提出要求,但是这将是正确的水平故障数据,所有三个客户都应该拥有它。

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 
    ) 

我们可以利用这个子查询来构建该表中的行分成组为每一个客户在我们主要的查询使用其他子查询。

最后,我们可以使用分析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. 显然我有硬编码在子查询的客户端标识符。 这将有可能避免硬编码,但示例查询已经足够复杂了。
  2. 此查询不搜索 三胞胎。如果在10分钟的时间内A,B和C中的每一个都出现一次故障,那么每个CLIENT在窗口内发生多少次实例并不重要。您的业​​务规则中没有任何内容可以说这是错误的。
  3. 同样,一个CLIENT的相同实例 可以与重叠窗口中其他CLIENT的实例相匹配。现在这可能是不可取的:双击或三重计数可能会使FAIL_COUNT膨胀。但同样,处理这个将会使最终查询更加复杂。
  4. 查询所呈现的查询对于A,B和C FAIL_TIME值的每个不同组合都有一行。如果您确实需要为每个CLIENT/FAIL_TIME设置一行,则可以调整结果集。