如何引用子查询中的外部表以进行自我左连接?

时间:2014-07-02 17:56:05

标签: sql oracle self-join correlated-subquery

使用此架构:

CREATE TABLE BAR (id INT PRIMARY KEY);
CREATE TABLE FOO (id INT PRIMARY KEY, rank INT UNIQUE, fk INT, FOREIGN KEY (fk) REFERENCES Bar(id));

INSERT INTO BAR (id) VALUES (1);
INSERT INTO BAR (id) VALUES (2);

-- sample values
INSERT INTO FOO (id, rank, fk) VALUES (1, 10, 1);
INSERT INTO FOO (id, rank, fk) VALUES (2, 3, 1);
INSERT INTO FOO (id, rank, fk) VALUES (3, 9, 2);
INSERT INTO FOO (id, rank, fk) VALUES (4, 5, 1);
INSERT INTO FOO (id, rank, fk) VALUES (5, 12, 1);
INSERT INTO FOO (id, rank, fk) VALUES (6, 14, 2);

如何查询FOO 的某些行,这些行与BAR的同一行相关联,排在rank的下一行?也就是说,我想搜索某些行(“目标”),并且对于每个目标行,我还想找到另一行(“辅助”),这样辅助具有secondary.fk = target.fk的所有行的最高等级 secondary.rank < target.rank

例如,如果我定位所有行(没有where子句),我会期望这个结果:

TARGET_ID  TARGET_RANK  SECONDARY_ID  SECONDARY_RANK
---------  -----------  ------------  --------------
        1           10             4               5
        2            3          NULL            NULL
        3            9          NULL            NULL
        4            5             2               3
        5           12             1              10
        6           14             3               9

当目标行有id 2或3时,没有辅助行,因为没有行与目标行具有相同的fk和较低的rank

我试过了:

SELECT F1.id AS TARGET_ID, F1.rank as TARGET_RANK, F2.id AS SECONDARY_ID, F2.rank AS SECONDARY_RANK
FROM FOO F1
LEFT JOIN FOO F2 ON F2.rank = (SELECT MAX(S.rank)
                               FROM FOO S
                               WHERE S.fk = F1.fk
                                 AND S.rank < F1.rank);

...但我得到ORA-01799: a column may not be outer-joined to a subquery

接下来我尝试了这个:

SELECT F1.id AS TARGET_ID, F1.rank AS TARGET_RANK, F2.id AS SECONDARY_ID, F2.rank AS SECONDARY_RANK
FROM FOO F1
LEFT JOIN (SELECT S1.rank, S1.fk
           FROM FOO S1
           WHERE S1.rank = (SELECT MAX(S2.rank)
                            FROM FOO S2
                            WHERE S2.rank < F1.rank
                              AND S2.fk = F1.fk)
           ) F2 ON F2.fk = F1.fk;

...但我得到ORA-00904: "F1"."FK": invalid identifier

肯定有一些方式在单个查询中执行此操作?

3 个答案:

答案 0 :(得分:1)

它不喜欢临时表中的子查询。诀窍是连接所有次要行,rank小于目标的rank,然后使用WHERE子句过滤除最大值以外的所有行,同时确保不过滤掉没有辅助的目标行

select F1.id as TARGET_ID, F1.rank as TARGET_RANK, F2.id as SECOND_ID, F2.rank as SECOND_RANK
from FOO F1
left join FOO F2 on F1.fk = F2.fk and F2.rank < F1.rank
where F2.rank is null or F2.rank = (select max(S.rank)
                                    from FOO S
                                    where S.fk = F1.fk
                                      and S.rank < F1.rank);

答案 1 :(得分:0)

您需要Analytic Functions

select id, 
lead(rank) over (partition by fk order by rank) next_rank
lag(rank) over (partition by fk order by rank)  prev_rank
from foo

它比自联接更有效。

但是如果你想折磨你的数据库,你可以尝试

select id, 
(select min(f2.rank) from foo f2 where f2.fk = f1.fk and f2.rank >f1.rank) next_rank,
(select max(f2.rank) from foo f2 where f2.fk = f1.fk and f2.rank <f1.rank) prev_rank
from foo f1

好的,我误解了输出中需要的东西。这是一个例子:

select id, rank, prev_rank_id from (
select id, rank,
lag(id) over (partition by fk order by rank) prev_rank_id
from foo
)
where id in (1, 3)

Analitic函数正在处理OUTPUT数据集。您需要将其包装到另一个select语句中以限制实际输出。

答案 2 :(得分:0)

select f1.rank f1rank, f2.rank as f2rank
from foo f1
left join foo f2 on f1.fk = f2.fk and f1.rank > f2.rank

这将为每个foo记录返回一行,该记录在同一个表中有另一条记录(使用fk确定)并且低于现有的等级。现在你只需要那里的max(f2.rank)。将上方转入子查询并找到最大值

Select f1rank, max(f2rank) f2rank
from
(select f1.rank f1rank, f2.rank as f2rank
from foo f1
left join foo f2 on f1.fk = f2.fk and f1.rank > f2.rank) a
group by f1rank

抱歉,我没有时间测试/排除故障,但逻辑应该在那里。