【问题标题】:Convert Subquery to Self Join将子查询转换为自联接
【发布时间】:2019-02-14 20:13:22
【问题描述】:

SQL 新手,我知道联接往往比子查询快。我有下表,我当前的查询给出了我需要的结果,但我无法围绕一个使用自联接的类似查询,假设它是可能的。

表格

id           scheduled_id action_id
------------ ------------ ------------
1            1            1
2            1            2
3            1            3
4            2            1
5            2            2
6            3            1

架构

create table ma (
id integer primary key,
scheduled_id integer,
action_id integer
);

insert into ma (
id,
scheduled_id,
action_id
)
values
(1, 1, 1),
(2, 1, 2),
(3, 1, 3),
(4, 2, 1),
(5, 2, 2),
(6, 3, 1);

查询

select * from ma where action_id = 3
union all
select * from ma where scheduled_id not in (
  select scheduled_id from ma
  where action_id = 3)

结果

id           scheduled_id action_id
------------ ------------ ------------
3            1            3
4            2            1
5            2            2
6            3            1

我的结果应该是 action_id 值为 3 的所有行加上那些 schedule_id 的 action_id 值不为 3 的所有行。

可以在http://sqlfiddle.com/#!5/0ba51/3 找到 sqlfiddle。

谢谢。

【问题讨论】:

  • 您说 我的结果应该是所有值为 3...的 schedule_ids 但在您的查询中您有 select * from ma where action_id = 3。哪个是正确的?
  • @forpas 我也对此感到困惑。鉴于结果表,我认为他们必须意味着他们想要任何具有 action_id = 3 的行以及具有 schedule_id 的所有行,其中 schedule_id 没有 action_id = 3 的行。(必须有更好的说法,但这是我现在能做的最好的了。)
  • 对不起,你是对的;我已经更新了 OP。

标签: sql sqlite subquery self-join


【解决方案1】:

您使用自联接寻找的结果是:

SELECT DISTINCT t1.*
FROM ma t1
JOIN ma t2  
ON  t1.SCHEDULED_ID <> t2.SCHEDULED_ID --Satisfies 2nd query
WHERE t2.ACTION_ID = 3 --Satisfies 2nd query
    OR  t1.ACTION_ID = 3 --Satisfies 1st query
ORDER BY t1.ID

【讨论】:

  • 虽然我的问题有很多意想不到的好答案,而且我对 SQL 的了解还不够,无法在这里做出好的评估,但这个查询对我来说是最容易理解的, 是一个自连接(虽然这可能不是真正需要的)并且只需要两次表扫描(此时没有索引)。
  • @Bink 很高兴我能帮上忙! :)
【解决方案2】:

我认为 JOIN 并不是你真正需要的。我会使用以下查询,它可以避免 UNION :

SELECT m.* 
FROM ma m
WHERE 
    m.action_id = 3
    OR NOT EXISTS (
        SELECT 1
        FROM ma m1
        WHERE 
            m1.scheduled_id = m.scheduled_id
            AND m1.action_id = 3
    )

在检查某物是否存在(或不存在)时,带有相关子查询的 NOT EXISTS 通常是最相关和最有效的方法。

【讨论】:

    【解决方案3】:
    SELECT m1.* 
    FROM ma m1
    INNER JOIN
    (
        SELECT * 
        FROM ma m2 
        WHERE m2.action_id = 3
    ) AS matbl 
    WHERE m1.action_id = 3 
    OR matbl.scheduled_id<>m1.scheduled_id
    

    希望它会有所帮助。

    【讨论】:

      【解决方案4】:

      这个怎么样?虽然不是 self join 但比 union 快

      select * from ma
      where action_id = 3 or scheduled_id not in (
          select scheduled_id from ma
          where action_id = 3
        )
      

      【讨论】:

        【解决方案5】:

        我的结果应该是值为 3 的所有 schedule_ids 加上那些不为 3 的 schedule_ids 的所有 schedule_ids 和 action_ids。

        这不是您的查询所做的。执行此操作的查询是:

        select ma.*
        from ma
        where exists (select 1
                      from ma ma2
                      where ma2.scheduled_id = ma.scheduled_id and
                            ma2.action_id = 3
                     );
        

        虽然您可以使用自联接来执行此操作,但这很棘手,因为查询可能会导致重复。对于逻辑,我推荐existsin

        【讨论】:

          【解决方案6】:

          此代码仅在您的 action_id 始终为 1、2、3、4 等且从不跳过 3 时才有效。我只是想提供一个替代答案,以防添加 max(action_id) 的概念对你。

          select ma.id
               , ma.scheduled_id
               , ma.action_id
               , ma_max.max_action_id
          from (
              select scheduled_id
                   , max(action_id) as max_action_id
              from ma
              group by scheduled_id
          ) ma_max
          join ma
              on ma_max.scheduled_id = ma.scheduled_id 
          where (action_id = 3 or max_action_id < 3)
          

          几乎可以肯定,它的性能不如使用“EXISTS”的其他答案。我只是喜欢在where (action_id = 3 or max_action_id &lt; 3) 中将逻辑的复杂性降低到基本上易于阅读的一行。

          【讨论】:

            猜你喜欢
            • 2012-04-03
            • 1970-01-01
            • 2021-09-16
            • 2020-12-06
            • 2010-10-10
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多