Oracle中的嵌套子查询很慢

时间:2013-07-21 02:14:09

标签: sql oracle

我有一个包含三个字段的表:IDdateactionaction的三个可能值为XYZ。我想要一个查询,对于表格中的每个ID,其中有Z行的行,将返回同一ID的最新行{{1}动作,如果存在的话。

这是我想在MySQL中做的事情。我复制并粘贴了sqlfiddle.com的代码,所以我知道它有效。

X

我不能在Oracle中使用create table a (id int, date int, action varchar(1)); insert into a values(1, 1, 'X'); insert into a values(1, 2, 'X'); insert into a values(1, 3, 'Z'); insert into a values(2, 1, 'X'); insert into a values(2, 2, 'Y'); insert into a values(2, 3, 'Y'); insert into a values(2, 4, 'Z'); insert into a values(3, 1, 'X'); insert into a values(3, 2, 'Y'); insert into a values(3, 3, 'X'); insert into a values(3, 4, 'Z'); insert into a values(4, 3, 'X'); SELECT a.id, max(a.date) as Xdate, b.date as Zdate, a.action FROM a, (SELECT * FROM a WHERE a.action = 'Z') b WHERE a.date < b.date AND a.ID = b.ID AND a.action = 'X' GROUP BY a.id; 子句和其他类似的字段,所以我做了一个嵌套的子查询,它分别找到了每个ID的最大日期,但它很慢(我希望得到大约10 ^ 5行)我希望有更好的方法来做到这一点会更快。 (我现在无法发布我的实际Oracle查询,因​​为它不能在SQLfiddle中运行;它一直在抱怨行被明确定义。)

上面的查询可以以某种方式在Oracle中工作吗?如果没有,是否有相同的方法可以在合理的时间内运行?

3 个答案:

答案 0 :(得分:2)

这有效(尽管可以简化):

SELECT a.id,
       max(a."date") as Xdate,
       b."date" as Zdate,
       a.action 
FROM a INNER JOIN 
  (SELECT * 
   FROM a 
   WHERE a.action = 'Z') b
  ON a.ID = b.ID 
WHERE a."date" < b."date" 
   AND a.action = 'X'
GROUP BY a.id, b."date", a.action

请注意,您需要在保留字周围使用" - 在本例中为date列。您还需要将所有字段添加到GROUP BY子句中。 MySQL允许它没有,但Oracle没有。


编辑,简化版:

SELECT a.Id, max(a."date"), b."date", a.action
FROM a
  INNER JOIN a b ON a.Id = b.Id AND b.action = 'Z'
WHERE a.action = 'X' 
  AND a."date" < b."date"
GROUP BY a.Id, b."date", a.action

答案 1 :(得分:1)

这里有其他解决方案:

WITH temp AS (
     SELECT a.id, a.date_f, a.action FROM a WHERE a.action = 'Z'
)
SELECT a.id,
       max(a.date_f) as Xdate,
       temp.date_f as Zdate,
       a.action
FROM a
INNER JOIN temp ON temp.id = a.id AND a.date_f < temp.date_f
WHERE a.action = 'X'
GROUP BY a.id,temp.date_f,a.action;

注意:date_f是字段日期。 的 SQL Fiddle Demo

答案 2 :(得分:0)

尝试在MySQL和Oracle之间进行的一个问题是Oracle功能更强大。

您可以使用Oracle中的分析功能解决您的问题。这些在MySQL中不可用。

以下是Oracle解决方案:

select a.id, a.thedate as Xdate, a.ZDate
from (select a.*,
             min(case when action = 'Z' then thedate end) over
                 (partition by id
                  order by thedate
                  rows between current row and unbounded following
                 ) as Zdate,
             row_number() over (partition by id, action
                                order by thedate desc
                               ) as seqnum
      from a
     ) a
where a.action = 'X' and a.seqnum = 1;

您可以在SQL Fiddle here上看到它。