【问题标题】:LEFT OUTER JOIN when joining two tables连接两个表时的 LEFT OUTER JOIN
【发布时间】:2013-11-28 17:53:52
【问题描述】:

当第三个表t3 上存在给定条件时,我想将第二个表t2 左外连接到第一个表t1,该表已连接到第二个表t2。如果该条件不存在,我仍然希望从第一个表 t1 返回记录,就像 LEFT OUTER JOIN 通常的工作方式一样。

我可能已经把阅读这篇文章的人完全搞糊涂了,所以我写了两个返回我想要的结果的查询。

一个查询比另一个更有效吗?是否有更高效的查询返回相同的结果?

http://sqlfiddle.com/#!2/bd56e6/8

SELECT *
FROM t1
LEFT OUTER JOIN
(
    SELECT t2.t1_id,t3.fk
    FROM t2
    INNER JOIN t3 ON t3.id=t2.t3_id
    WHERE t3.fk=123
) AS t2 ON t2.t1_id=t1.id

ID  DATA    T1_ID   FK
1   blue    1   123
2   red     (null)  (null)
3   green   (null)  (null)

解释

ID  SELECT_TYPE     TABLE   TYPE    POSSIBLE_KEYS   KEY     KEY_LEN     REF     ROWS    EXTRA
1   PRIMARY     t1  ALL     (null)  (null)  (null)  (null)  3   
1   PRIMARY     <derived2>  ALL     (null)  (null)  (null)  (null)  1   
2   DERIVED     t2  index   PRIMARY     fk_table1_t11_idx   4   (null)  3   Using index
2   DERIVED     t3  ALL     PRIMARY     (null)  (null)  (null)  3   Using where; Using join buffer

http://sqlfiddle.com/#!2/bd56e6/5

SELECT *
FROM t1
LEFT OUTER JOIN
(
    SELECT t2.t1_id,t3.fk
    FROM t2
    INNER JOIN t3 ON t3.id=t2.t3_id 
) AS t2 ON t2.t1_id=t1.id AND t2.fk=123 

ID  DATA    T1_ID   FK
1   blue    1   123
2   red     (null)  (null)
3   green   (null)  (null)

解释

ID  SELECT_TYPE     TABLE   TYPE    POSSIBLE_KEYS   KEY     KEY_LEN     REF     ROWS    EXTRA
1   PRIMARY     t1  ALL     (null)  (null)  (null)  (null)  3   
1   PRIMARY     <derived2>  ALL     (null)  (null)  (null)  (null)  3   
2   DERIVED     t2  index   PRIMARY     fk_table1_t11_idx   4   (null)  3   Using index
2   DERIVED     t3  ALL     PRIMARY     (null)  (null)  (null)  3   Using where; Using join buffer

您将看到以下两个查询产生不同的结果:

http://sqlfiddle.com/#!2/bd56e6/9

SELECT *
FROM t1
LEFT OUTER JOIN t2 ON t2.t1_id=t1.id
LEFT OUTER JOIN t3 ON t3.id=t2.t3_id
WHERE t3.fk=123

ID  DATA    T3_ID   T1_ID   FK
1   blue    1   1   123

http://sqlfiddle.com/#!2/bd56e6/10

SELECT *
FROM t1
LEFT OUTER JOIN t2 ON t2.t1_id=t1.id
LEFT OUTER JOIN t3 ON t3.id=t2.t3_id AND t3.fk=123

ID  DATA    T3_ID   T1_ID   FK
1   blue    1   1   123
2   red     2   2   (null)
2   red     3   2   (null)
3   green   (null)  (null)  (null)

我的架构在下面

CREATE  TABLE IF NOT EXISTS t1 (
  id INT NOT NULL ,
  data VARCHAR(45) NULL ,
  PRIMARY KEY (id) )
ENGINE = InnoDB;

CREATE  TABLE IF NOT EXISTS t3 (
  id INT NOT NULL ,
  fk VARCHAR(45) NULL ,
  PRIMARY KEY (id) )
ENGINE = InnoDB;

CREATE  TABLE IF NOT EXISTS t2 (
  t3_id INT NOT NULL ,
  t1_id INT NOT NULL ,
  data VARCHAR(45) NULL ,
  PRIMARY KEY (t3_id) ,
  INDEX fk_table1_t11_idx (t1_id ASC) ,
  CONSTRAINT fk_table1_t3
    FOREIGN KEY (t3_id )
    REFERENCES t3 (id )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT fk_table1_t11
    FOREIGN KEY (t1_id )
    REFERENCES t1 (id )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

INSERT INTO t1(id,data) VALUES (1,'blue');
INSERT INTO t1(id,data) VALUES (2,'red');
INSERT INTO t1(id,data) VALUES (3,'green');

INSERT INTO t3(id,fk) VALUES (1,123);
INSERT INTO t3(id,fk) VALUES (2,321);
INSERT INTO t3(id,fk) VALUES (3,321);

INSERT INTO t2(t1_id,t3_id,data) VALUES (1,1,'dog');
INSERT INTO t2(t1_id,t3_id,data) VALUES (2,2,'Cat');
INSERT INTO t2(t1_id,t3_id,data) VALUES (2,3,'Bird');

【问题讨论】:

  • 添加一些样本和您的预期结果会很有帮助
  • 如果不使用子选择,这可能会更快。
  • 尝试对两个查询运行解释计划并比较该输出...我认为查询计划应该相同。
  • @Axel。我怎么会不使用子选择?它不会过滤 t1 吗?
  • @user1032531 “过滤器 t1”是什么意思?您使用左连接,因此 t1 中的所有内容都会显示出来。

标签: mysql sql


【解决方案1】:

玩弄这样的东西(已经很晚了,所以我可能有点搞砸了)。但是,您必须更正条件 ON t3.id=td.t3_id,因为我在您的代码和我的代码中都没有看到任何名为 td... 的表:

SELECT t1.*,t2.t1_id,t3.fk
FROM t1
LEFT OUTER JOIN t2 ON t2.t1_id=t1.id
LEFT JOIN t3 ON t3.id=td.t3_id -- please update this condition
WHERE t3.fk=123

阅读您上面的评论可能是您正在寻找这样的东西:

SELECT t1.*,t2.t1_id,t3.fk
FROM t1
LEFT OUTER JOIN t2 ON t2.t1_id=t1.id
LEFT JOIN t3 ON t3.id=td.t3_id AND t3.fk=123 -- please update this condition

【讨论】:

  • 抱歉,td 是一个错字。我认为您的第二个查询可能有效。我将对其进行测试并发布一个 sql fiddle。
  • 阿克塞尔,请看我的原帖。我展示了这两个查询以及它们如何产生不同的结果。
【解决方案2】:

一个查询比另一个更有效吗?

不,它们生成相同的查询计划,因此它们应该具有相同的性能。不假设可以从查询缓存或 InnoDB 缓冲池中获取结果..

是否有更高效的查询返回相同的结果?

好吧,您确实问过这个问题,这个查询可能会将您推向正确的方向... 因为我不确定你是否需要这个..

SELECT
  t1.id
, t1.data
, IF(t3.fk IS NULL, NULL, t1.id) "t1_id"
, t3.fk
FROM
  t1

LEFT JOIN (
  SELECT 
    t1_id
  FROM 
    t2
  GROUP BY
    t1_id ASC
)
 t2
ON
 t1.id = t2.t1_id

LEFT JOIN 
 t3
ON
 t3.id = t2.t1_id AND t3.fk = '123'

查看演示http://sqlfiddle.com/#!2/bd56e6/136

上面的查询还解决了查询中的重复 http://sqlfiddle.com/#!2/bd56e6/173

需要一个较少派生的表并将类型更改为 eq_ref.. 这将带来更好的性能,因为可以使用更少的内存进行更好的查找.. 请注意,无法删除全表扫描在表 t1..

您的要求/问题..

我想将第二个表 t2 左外连接到第一个表 t1 当第三个表 t3 上存在给定条件时,该表 t3 连接到 第二张表t2。

但对我来说,第一条记录听起来更像是一种关系(INNER JOIN),因为这是基于条件 t3.fk = '123'..的匹配记录..

如果那个条件不存在,我仍然希望 从第一个表 t1 返回记录,就像 LEFT OUTER JOIN 正常工作。

下一个记录是表 t1 中的不匹配项...是什么让我想知道理论上应该可以编写一个更好的优化查询..

待续……

【讨论】:

  • 谢谢雷。 The query above also resolves an duplication within your query: 被复制的行并没有真正被复制,因为它包含不同的 t3。 sqlfiddle.com/#!2/bd56e6/191But still the first record sounds more like an relationship (INNER JOIN) to me because this is an matching record based on condition t3.fk = '123'.. 但是 INNER JOIN 会过滤掉任何我不想要的未加入的 T1。 to be continued...期待。
  • @user1032531 确实与您的新 sqlfiddle www.sqlfiddle.com/#!2/bd56e6/191 一起使用。它不再重复了。新的 sqlfiddle 显示了您需要的列?
  • 是的,我想我得到了我需要的结果,并且应该保留我的原始查询之一。不过不知道有没有更好的。也许在 WHERE 子句中,如果没有 JOIN,某种条件 IF 语句不会过滤 T1?
  • WHERE 子句中的 IF 语句将使该列上的索引无用
猜你喜欢
  • 1970-01-01
  • 2014-07-19
  • 1970-01-01
  • 2012-06-21
  • 2014-10-23
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 2014-08-10
相关资源
最近更新 更多