【问题标题】:SQL: "NOT IN" alternative for selecting rows based on values of *different* rows?SQL:基于*不同*行的值选择行的“NOT IN”替代方法?
【发布时间】:2009-08-17 04:29:39
【问题描述】:

如何创建一个 SQL 语句来返回由子查询、连接或其他东西修改的结果,以处理您试图返回的信息?

例如:

CREATE TABLE bowlers (
bowling_id int4 not null primary key auto_increment,
name text,
team text
);

某人可能错误地加入了多个团队:

INSERT INTO `bowlers` (`name`, `team`) VALUES
('homer', 'pin pals'),
('moe', 'pin pals'),
('carl', 'pin pals'),
('lenny', 'pin pals'),
('homer', 'The homer team'),
('bart', 'The homer team'),
('maggie', 'The homer team'),
('lisa', 'The homer team'),
('marge', 'The homer team'),
('that weird french guy', 'The homer team');

所以homer 无法决定他的团队,所以他在两者上都有。干!

我想知道the homer team 不在pin pals 团队中的每个人。我能做的最好的就是:

SELECT a.name, a.team 
    FROM bowlers a where a.team = 'The homer team' 
    AND a.name 
    NOT IN (SELECT b.name FROM bowlers b WHERE b.team = 'pin pals');

导致:

+-----------------------+----------------+
| name                  | team           |
+-----------------------+----------------+
| bart                  | The homer team | 
| maggie                | The homer team | 
| lisa                  | The homer team | 
| marge                 | The homer team | 
| that weird french guy | The homer team | 
+-----------------------+----------------+
5 rows in set (0.00 sec)

你知道,太棒了!

性能会受到影响,因为子查询将针对查询的每个结果运行,即 B 到 A 到 D。对于几行来说很好,对于数十万行。

有什么更好的方法?我主要认为自我加入可以解决问题,但我不知道如何做到这一点。

有没有其他方法可以做到这一点,而不使用,NOT IN( SELECT ... )

另外,这类问题的名称是什么?

【问题讨论】:

  • 左外连接回到自己是你想要的。

标签: mysql performance subquery


【解决方案1】:

像这样:

SELECT a.name, a.team
FROM bowlers a
LEFT OUTER JOIN bowlers b ON a.name = b.name AND b.team = 'pin pals'
WHERE a.team = 'The homer team'
AND b.name IS NULL;

你也可以这样做:

SELECT a.name, a.team
FROM bowlers a
WHERE a.team = 'The homer team'
AND NOT EXISTS (SELECT * FROM bowlers b
    WHERE b.team = 'pin pals'
    AND a.name = b.name
    );

顺便说一下,这被称为“左反半连接”。

【讨论】:

  • 太棒了!您的第一个示例当然是对我的问题的改进。仍然运行有点慢(MySQL 版本 5.0.37),但有一些......好吧,至少现在它正在返回!感谢 (Left Anti-Semi Join) 用术语来解释这一点
【解决方案2】:

您可以LEFT JOIN 并确保连接的表中没有数据(一切都为空)。

SELECT a.name, a.team 
    FROM bowlers a
    LEFT JOIN bowlers b
        ON b.name = a.name AND b.team = 'pin pals'
    WHERE a.team = 'The homer team' 
    AND a.name
    -- the join has to fail for this to be null
    AND b.bowling_id IS NULL

【讨论】:

  • 这绝对有效,但它比他的子查询效率更高还是更低?似乎加入时会有更多开销,但我不知道。
  • 高度依赖表结构,有哪些索引,表的行数等,A队或B队有多少人等。
猜你喜欢
  • 1970-01-01
  • 2011-11-19
  • 2013-11-18
  • 2013-05-29
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
  • 2020-08-26
  • 2021-09-18
相关资源
最近更新 更多