查找基于集合的解决方案,而不是循环遍历SQL中的每一行

时间:2016-04-01 23:04:13

标签: sql oracle

我有一张断电信息表,看起来像这样,

event_ID      event_timestamp     event_info        POWER LINE ID
44            1/5 1:45 pm         power on          247
45            1/5 1:45 pm         power on          247
46            1/5 3:45 pm         fault detected    247
47            1/5 3:55 pm         power off         247
48            1/5 3:58 pm         power off         247
49            1/5 5:15 pm         power on          247
50            1/5 5:45 pm         power off         247
51            1/5 5:50 pm         power off         247
52            1/5 5:55 pm         power off         247
53            1/5 5:59 pm         power off         247

KEY只是表格的主键。停电时间告诉我们停电发生的时间,电力线ID只是发生停电的线路的识别号码。

我有第二个包含仪表信息的表,看起来像这样,

KEY    event_ID   POWER LINE ID
2      46         247
2      47         247
2      48         247
3      50         247
3      51         247
3      52         247
3      53         247

目标如下:对于每次停电,选择在停电时间之前以及最近的电源开启后在该电力线上发生的所有仪表事件。在该电力线上发生的信号。

例如,对于停机#2,我们将在1/5下午4:00(停电时间)之前查看所有仪表事件,但是在1/5 1:45 pm之后发生的所有事件,因为这是最新的"开机"停电前发生的信号。对于停机#3,我们会查看在下午1/5下午6:00(停电时间)之前发生的所有事件,但是在1/5下午5:15之后,因为这是最近的"电源开启&# 34;在停电时间之前发生的信号。

我使用游标循环停止表并调用从事件表中选择所需事件的存储过程,但是这些表非常大,并且光标花费的时间太长。我想知道一种基于集合的方法来解决这个问题。谢谢!

编辑:对不起,我完全忘了发布示例输出。在这里。

using MySql.Data.MySqlClient;
using System.Data;

编辑(再次):我正在Oracle中寻找解决方案。我很抱歉编辑,第一次在这里发帖提问。

5 个答案:

答案 0 :(得分:1)

我正在使用SQL Server解决问题,因此#表示临时表。

假设下面的表和数据

create table #outage ([key] int, outage_time datetime, power_line int)

insert into #outage values 
(1,       '2015/1/1 2:30 pm',     75),
(2,       '2015/1/5 4:00 pm',    247),
(3,       '2015/1/5 6:00 pm',    247),
(4,       '2015/1/3 8:00 am',     11)

create table #even (event_ID int , event_time datetime, 
                    event_info varchar(20), power_line int)

insert into #even values
(44,            '2015/1/5 1:45 pm'         ,'power on'          ,247),
(45,            '2015/1/5 1:45 pm'         ,'power on'          ,247),
(46,            '2015/1/5 3:45 pm'         ,'fault detected'    ,247),
(47,            '2015/1/5 3:55 pm'         ,'power off'         ,247),
(48,            '2015/1/5 3:58 pm'         ,'power off'         ,247),
(49,            '2015/1/5 5:15 pm'         ,'power on'          ,247),
(50,            '2015/1/5 5:45 pm'         ,'power off'         ,247),
(51,            '2015/1/5 5:50 pm'         ,'power off'         ,247),
(52,            '2015/1/5 5:55 pm'         ,'power off'         ,247),
(53,            '2015/1/5 5:59 pm'         ,'power off'         ,247)

这是查询:

select o.[key], e.event_ID, o.power_line
from #outage o
    inner join #even e on e.power_line = o.power_line
    and e.event_time < o.outage_time
    and e.event_time > (select max(event_time) from #even 
                        where power_line = o.power_line 
                            and event_time < o.outage_time 
                            and event_info = 'power on')

答案 1 :(得分:1)

试试这个:

SELECT power_outage.key,
       meters.event_id,
       power_outage.power_line_id
FROM   power_outage
JOIN   meter_info meters
  ON   power_outage.power_line_id = meters.power_line_id
 AND   meters.event_timestamp < power_outage.outage_time
WHERE  meters.event_timestamp > (SELECT MAX(lpo.event_timestamp)
                                 FROM   meter_info lpo -- LastPowerOn
                                 WHERE  lpo.power_line_id = power_outage.power_line_id
                                   AND  lpo.event_info = 'power on'
                                   AND  lpo.event_timestamp < power_outage.outage_time);

“JOIN”会在中断时间之前获取所有元素,而条件会过滤与最近一次上电相对应的元素。

答案 2 :(得分:1)

这是一个涉及CTE的较长查询,但逐步更容易理解:

WITH events_before_outage AS (
select
  ot.key, ot.outage_time, ot.power_line_id, mi.event_id, mi.event_timestamp, mi.event_info
from outage_table ot
left join meter_information mi
  on ot.power_line_id = mi.power_line_id
  and ot.outage_time > mi.event_timestamp
)
, last_power_on AS (
select key, max(event_timestamp) as event_date
from events_before_outage
where event_info = 'power on'
group by 1
)
select a.key, a.event_id, a.power_line_id
from events_before_outage a
where a.event_timestamp > ( select event_date from last_power_on b where a.key = b.key )
order by 1,2

输出:

 key | event_id | power_line_id
-----+----------+---------------
   2 |       46 |           247
   2 |       47 |           247
   2 |       48 |           247
   3 |       50 |           247
   3 |       51 |           247
   3 |       52 |           247
   3 |       53 |           247

答案 3 :(得分:0)

这是一种使用分析函数的完全基于集合的方法。这个想法是通过对它们进行累积计数来枚举“poweron”来获得组。然后,使用此组获取组中任何断电的时间,并返回值。

select om.*
from (select om.*,
             max(om.outagetime) over (partition by poweron_grp, powerlineid) as outagetime,
             min(event_timestamp) over (partition by powerongrp, powerlineid as minet,
             max(event_timestamp) over (partition by powerongrp, powerlineid as maxet
      from (select m.*, o.outagetime,
                   sum(case when m.event_info = 'power on' then 1 else 0 end) over
                       (partition by m.powerlineid order by m.event_timestamp
                       ) as poweron_grp
            from outages o join
                 meters m
            on o.powerlineid = m.powerlineid
           ) om
     ) om
where outagetime between minte and maxte and
      event_timestamp < outagetime;

答案 4 :(得分:0)

一些建议的答案使用完整的仪表信息表使用相关的子查询,即使它们只需要event_info ='power on'的行。对每个连续的行计算相关的子查询,因此所有这些不需要的行都会被评估 - 并被丢弃 - 多次。因此,如果有很多事件的event_info与'power on'不同,这似乎就是这种情况,那么只需隔离'开机'事件就可以实现额外的效率。

这是一种方法。我给表格命名为outage_data和meter_data,在outage_data表中我将“key”列命名为outage_ID;使用关键字作为列名是一个非常糟糕的主意,而key ...是...一个关键字!子查询(cte)p被评估一次,它只是使用event_info ='power on'收集事件,相关子查询是针对p的,而不是针对完整的meter_data表。

with p as (select power_line, event_time from meter_data where event_info = 'power on')
select   o.outage_ID, m.event_ID, o.power_line
from     outage_data o join meter_data m on m.power_line = o.power_line
where    m.event_time <= o.outage_time 
  and    m.event_time > (select max(p.event_time) from p 
                      where p.power_line = o.power_line and p.event_time <= o.outage_time)
order by o.outage_ID, m.event_ID