Oracle SELECT 对多列有多个 AND 条件(INTERSECT 替代)
Oracle SELECT with multiple AND conditions for multiple columns (INTERSECT alternative)
如何解决以下问题:
假设我们有一座大型建筑物,大约有 100 个温度读取器,每个读取器每分钟收集一次温度。
我有相当大的 table (~100m) 行,其中包含以下列:
Table 临时事件:
Timestamp - one entry per minute
Reader ID - about 100 separate readers
Temperature - Integer (-40 -> +40)
Timestamp 和 Reader ID 是 table 的主键和辅助键。我想执行一个查询,查找所有时间戳,其中
reader_01 = 10 度,
reader_02 = 15 度和
reader_03 = 20 度。
换句话说是这样的:
SELECT Timestamp FROM TempEvents
WHERE (readerID=01 AND temperature=10)
AND (readerID=02 AND temperature=15)
AND (readerID=03 AND temperature=20)
==> 生成时间戳列表:
Timestamp::
2016-01-01 05:45:00
2016-02-01 07:23:00
2016-03-01 11:56:00
2016-04-01 23:21:00
上面的查询 returns 没有任何内容,因为一行没有同时包含所有条件。在条件之间使用 OR 也不会产生预期的结果,因为所有读取器都应该匹配条件。
使用 INTERSECT,我可以通过以下方式得到结果:
SELECT * FROM
(SELECT Timestamp FROM TempEvents WHERE readerID=01 AND temperature=10
INTERSECT SELECT Timestamp FROM TempEvents WHERE readerID=02 AND temperature=15
INTERSECT SELECT Timestamp FROM TempEvents WHERE readerID=03 AND temperature=20
)
GROUP BY Timestamp ORDER BY Timestamp ASC;
上述查询非常耗时,需要大约 5 分钟才能执行。
有没有更好(更快)的方法得到结果?
如果您要查询的读者数量不是太多,您可以尝试使用 join
-查询,例如
select distinct Timestamp
from TempEvents t1
join TempEvents t2 using(Timestamp)
join TempEvents t3 using(Timestamp)
where t1.readerID=01 and t1.temperature = 10
and t2.readerID=02 and t2.temperature = 15
and t3.readerID=03 and t3.temperature = 20
但老实说,我怀疑它的性能是否会比您的 INTERSECT
-查询更好。
我刚刚在 Oracle DB 中尝试了这个,它似乎有效:
SELECT Timestamp FROM TempEvents
WHERE (readerID=01 AND temperature=10)
OR (readerID=02 AND temperature=15)
OR (readerID=03 AND temperature=20)
确保只更改括号外的 AND
试试这个:
with Q(readerID,temperature) as(
select 01, 10 from dual
union all
select 02,15 from dual
union all
select 03,20 from dual
)
select Timestamp FROM TempEvents T, Q
where T.readerID=Q.readerID and T.temperature=Q.temperature
group by Timestamp
having count(1)=(select count(1) from Q)
也许这会提供比使用 OR
或 IN
子句更好的计划。
如何解决以下问题:
假设我们有一座大型建筑物,大约有 100 个温度读取器,每个读取器每分钟收集一次温度。
我有相当大的 table (~100m) 行,其中包含以下列:
Table 临时事件:
Timestamp - one entry per minute
Reader ID - about 100 separate readers
Temperature - Integer (-40 -> +40)
Timestamp 和 Reader ID 是 table 的主键和辅助键。我想执行一个查询,查找所有时间戳,其中
reader_01 = 10 度,
reader_02 = 15 度和
reader_03 = 20 度。
换句话说是这样的:
SELECT Timestamp FROM TempEvents
WHERE (readerID=01 AND temperature=10)
AND (readerID=02 AND temperature=15)
AND (readerID=03 AND temperature=20)
==> 生成时间戳列表:
Timestamp::
2016-01-01 05:45:00
2016-02-01 07:23:00
2016-03-01 11:56:00
2016-04-01 23:21:00
上面的查询 returns 没有任何内容,因为一行没有同时包含所有条件。在条件之间使用 OR 也不会产生预期的结果,因为所有读取器都应该匹配条件。
使用 INTERSECT,我可以通过以下方式得到结果:
SELECT * FROM
(SELECT Timestamp FROM TempEvents WHERE readerID=01 AND temperature=10
INTERSECT SELECT Timestamp FROM TempEvents WHERE readerID=02 AND temperature=15
INTERSECT SELECT Timestamp FROM TempEvents WHERE readerID=03 AND temperature=20
)
GROUP BY Timestamp ORDER BY Timestamp ASC;
上述查询非常耗时,需要大约 5 分钟才能执行。
有没有更好(更快)的方法得到结果?
如果您要查询的读者数量不是太多,您可以尝试使用 join
-查询,例如
select distinct Timestamp
from TempEvents t1
join TempEvents t2 using(Timestamp)
join TempEvents t3 using(Timestamp)
where t1.readerID=01 and t1.temperature = 10
and t2.readerID=02 and t2.temperature = 15
and t3.readerID=03 and t3.temperature = 20
但老实说,我怀疑它的性能是否会比您的 INTERSECT
-查询更好。
我刚刚在 Oracle DB 中尝试了这个,它似乎有效:
SELECT Timestamp FROM TempEvents
WHERE (readerID=01 AND temperature=10)
OR (readerID=02 AND temperature=15)
OR (readerID=03 AND temperature=20)
确保只更改括号外的 AND
试试这个:
with Q(readerID,temperature) as(
select 01, 10 from dual
union all
select 02,15 from dual
union all
select 03,20 from dual
)
select Timestamp FROM TempEvents T, Q
where T.readerID=Q.readerID and T.temperature=Q.temperature
group by Timestamp
having count(1)=(select count(1) from Q)
也许这会提供比使用 OR
或 IN
子句更好的计划。