【问题标题】:Left Outer Join via a link table, using min() to restrict join to one row通过链接表进行左外连接,使用 min() 将连接限制为一行
【发布时间】:2018-03-09 10:21:20
【问题描述】:

我正在尝试编写一个 Oracle SQL 查询来连接两个通过链接表链接的表(我的意思是一个有 2 列的表,每列都是主表的外键)。 min() 函数用于将左外连接的结果限制为单行。

我的模型由“父母”和“侄子”组成。父母可以有 0 个或多个侄子。父母可以启用或禁用。每个侄子都有一个生日日期。我的查询目标是:

为每个启用的父母打印一行,列出该父母最年长的侄子(即最小(生日)的侄子)。

我的问题在 sqlfiddle 有说明:http://sqlfiddle.com/#!4/9a3be0d/1

我可以形成一个查询,列出启用的父母的 所有 侄子,但这还不够好 - 我只希望每个父母有一行,其中仅包括 最老的 em>侄子。形成外部表的 where 子句似乎是我的绊脚石。

我的表格和示例数据:

create table parent (parent_id number primary key, parent_name varchar2(50), enabled int);
create table nephew  (nephew_id number primary key, birthday date, nephew_name varchar2(50));
create table parent_nephew_link  (parent_id number not null, nephew_id number not null);

parent table:
+----+-------------+---------+
| id | parent_name | enabled |
+----+-------------+---------+
| 1  | Donald      | 1       |
+----+-------------+---------+
| 2  | Minnie      | 0       |
+----+-------------+---------+
| 3  | Mickey      | 1       |
+----+-------------+---------+
  nephew table:
  +-----------+------------+-------------+
  | nephew_id | birthday   | nephew_name |
  +-----------+------------+-------------+
  | 100       | 01/01/2017 | Huey        |
  +-----------+------------+-------------+
  | 101       | 01/01/2016 | Dewey       |
  +-----------+------------+-------------+
  | 102       | 01/01/2015 | Louie       |
  +-----------+------------+-------------+
  | 103       | 01/01/2014 | Morty       |
  +-----------+------------+-------------+
  | 104       | 01/01/2013 | Ferdie      |
  +-----------+------------+-------------+
parent_nephew_link table:
+-----------+-----------+
| parent_id | nephew_id |
+-----------+-----------+
| 1         | 100       |
+-----------+-----------+
| 1         | 101       |
+-----------+-----------+
| 1         | 102       |
+-----------+-----------+
| 3         | 103       |
+-----------+-----------+
| 3         | 104       |
+-----------+-----------+

我的(不正确的)查询:

-- This query is not right, it returns a row for each nephew
select parent_name, nephew_name
from parent p
left outer join parent_nephew_link pnl
  on p.parent_id = pnl.parent_id
left outer join nephew n
  on n.nephew_id = pnl.nephew_id
where enabled = 1
--    I wish I could add this clause to restrict the result to the oldest 
--    nephew but p.parent_id is not available in sub-selects.
--    You get an ORA-00904 error if you try this:
-- and n.birthday = (select min(birthday) from nephew nested where nested.parent_id = p.parent_id)

我想要的输出是:

+-------------+-------------+
| parent_name | nephew_name |
+-------------+-------------+
| Donald      | Louie       |
+-------------+-------------+
| Mickey      | Ferdie      |
+-------------+-------------+

感谢您的建议! 约翰

markaaronky 的建议

我尝试使用 markaaronky 的建议,但这个 sql 也有缺陷。

-- This query is not right either, it returns the correct data but only for one parent
select * from (
  select parent_name, n.nephew_name, n.birthday
  from parent p
  left outer join parent_nephew_link pnl
    on p.parent_id = pnl.parent_id
  left outer join nephew n
    on n.nephew_id = pnl.nephew_id
  where enabled = 1
  order by parent_name, n.birthday asc
) where rownum <= 1

【问题讨论】:

  • Donald id = 3 引用parent_nephew_link 表中的nephew_id = 103 + 104,该表又引用103--&gt;Morty104--&gt;Ferdie。你能解释一下为什么你想在结果中得到 Donald ---&gt;Louie 而不是 MorthyFerdie 吗?我完全迷失了,我不明白这个逻辑。
  • 我的错误-我手动将数据从 sqlfiddle 转录到 stackoverflow 并意外反转了 Donald 和 Mickey。我已经更正了上面显示的数据。谢谢!

标签: oracle left-join outer-join


【解决方案1】:

为什么不: (1) 在您的 SELECT 语句中包含 n.birthday 来自 nephews 表 (2) 在查询中添加 ORDER BY n.birthday ASC (3) 还修改您的选择,使其只占用第一行?

我试图在 sqlfiddle 中为你写出来,但它似乎不喜欢表别名(例如,当我写 n.birthday 时它会引发错误),但我确信这在 Oracle 中是合法的,即使我我是一名 SQL Server 人员。

另外,如果我没记错的话,Oracle 没有像 SQL Server 那样的 SELECT TOP...您必须改为执行“WHERE ROWNUM = 1”之类的操作?同样的概念...您只是对结果进行排序,所以最老的侄子是第一行,而您只占第一行。

也许一个不受欢迎的副作用是你会在结果中得到生日和名字。如果这是不可接受的,我很抱歉。看起来您的问题已经有一段时间没有得到解答,这个解决方案至少应该让您有一个开始。

最后,由于您的生日列没有 NOT NULL 约束并且正在执行左外连接,您可以通过添加 AND n.birthday IS NOT NULL 使查询更安全

【讨论】:

    【解决方案2】:

    用途:

    select parent_name, nephew_name
    from parent p
    left outer join
    (
      SELECT pnl.parent_id, n.nephew_name
        FROM parent_nephew_link pnl
        join nephew n
        on n.nephew_id = pnl.nephew_id
        AND n.BIRTHDAY = ( 
            SELECT min( BIRTHDAY ) 
            FROM nephew n1
            JOIN parent_nephew_link pnl1
            ON pnl1.NEPHEW_ID = n1.NEPHEW_ID
            WHERE pnl1.PARENT_ID = pnl.PARENT_ID
        )
    ) ppp
    on p.parent_id = ppp.parent_id
    where p.enabled = 1
    

    演示:http://sqlfiddle.com/#!4/98758/23

    | PARENT_NAME | NEPHEW_NAME |
    |-------------|-------------|
    |      Mickey |       Louie |
    |      Donald |      Ferdie |
    

    【讨论】:

    • 嘿,谢谢!这看起来正是我想要的!
    • 如果您喜欢我的回答,请upvote and accept it,谢谢。
    猜你喜欢
    • 2010-10-04
    • 1970-01-01
    • 1970-01-01
    • 2012-09-20
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-02
    相关资源
    最近更新 更多