2013-07-21 458 views
0

我有一个包含三个字段的表:IDdateactionaction的三个可能值是X,YZ。我想要一个查询,对于其中有一行Z操作的表中的每个ID,将返回ID操作的最近行(如果存在X操作)。Oracle中的嵌套子查询很慢

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

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; 

我不能使用与甲骨文其他领域类似这样的GROUP BY条款,所以我做了一个嵌套子查询其发现单独的每个ID的最大日期,但速度很慢(我期待着得到大约10^5行),我希望有更好的方法来做到这一点会更快。 (我目前无法发布我的实际Oracle查询,因​​为它不会在SQLfiddle中运行;它总是抱怨行被不明确地定义)。

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

+0

您的查询不起作用,因为您无法使用名称'date'作为字段名称,无论如何,纠正后,查询不起作用。请更正它,然后发布正确的查询以了解您想要的内容。 –

+1

请尝试此查询[sqlfiddle](http://sqlfiddle.com/#!4/adc50/7) –

回答

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

对于'join'而不是子查询。我还会将'a。“日期” dasblinkenlight

+0

谢谢!这看起来不错。明天我上班时我会试试看。我知道日期的事情,但只要使用默认的MySQL 5.5.32选项,sqlfiddle似乎并不介意。 – Flounderer

+3

@Flounderer在使用MySQL来调试你的Oracle查询时要小心:MySQL通常对于基于查询的查询来说更加轻松。在演示查询时,您应该切换到您选择的RDBMS。 – dasblinkenlight

0

这里其他的解决办法:

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

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提琴here