【问题标题】:LEFT JOIN not working as expected with sub-queryLEFT JOIN 在子查询中没有按预期工作
【发布时间】:2012-08-09 10:05:08
【问题描述】:

我有下面的 SQL 查询:

SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id

它返回messages 表中的所有行(大约4000 行)以及来自numbers 表的其他列。到目前为止,这是我所期望的。

现在我将这个子查询左连接到另一个表,再次使用左连接:

SELECT message, sent_date
FROM (
    SELECT message, sent_date, user_id
    FROM messages
    LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id

但是,它只返回大约 200 行,所以很多都丢失了。由于这是一个左连接,我希望 table1 中的所有行都在结果中。有人能看出是什么问题吗?

编辑:

因此,这里有 3 个相关表(删除了不相关的列)以供参考:

CREATE TABLE IF NOT EXISTS `messages` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `message` text CHARACTER SET utf8 NOT NULL,
  `from_id` int(11) DEFAULT NULL,
  `sent_date` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `from_id` (`from_id`),
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=101553 ;

CREATE TABLE IF NOT EXISTS `numbers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `number` varchar(32) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6408 ;

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(256) CHARACTER SET utf8 DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2395 ;

【问题讨论】:

  • user_id 在表 messages 中吗?
  • 您在最后一个查询中是否有WHERE,但您没有包含在问题中?
  • 是否有可能在其他连接之间删除或更新了表中的某些行?
  • @ypercube,user_id 在表中numbers
  • @ypercube,最后一个查询中没有WHERE

标签: mysql sql subquery left-join


【解决方案1】:

您可以尝试其他方法来调试问题:

CREATE TEMPORARY table tmp1 AS SELECT message, sent_date, user_id
                               FROM messages
                                    LEFT JOIN numbers 
                                        ON messages. from_id = numbers.id;

然后看看这个查询是否有效。

 SELECT message, sent_date
 FROM tmp1 table1
      LEFT JOIN users 
          ON table1.user_id = users.id;

另外,对于您的情况,请确保中间没有其他插入或更新。否则使用事务。

【讨论】:

  • 是的,这确实有效。虽然我很想知道为什么带有子查询的等效查询不起作用?
  • 原来的还是不行?你确定你没有在结果集上意外设置一个限制(200)吗?
【解决方案2】:

table1 有时没有用户 ID - 所以它会为空,所以这些结果会丢失?

【讨论】:

  • user_id 确实有时为空,但由于 table1 在左侧,不应该包括所有结果吗?如果没有,如何修复查询?
  • 其实我说的是垃圾。您的查询窗口是否设置了结果限制?
  • @podiluska:我认为你是对的,最可能的原因是结果限制。 @Laurent,如果将SELECT message, sent_date FROM (... 替换为SELECT COUNT(*) FROM (...,结果如何?
【解决方案3】:

我对你的问题没有确切的答案,但如果我必须开始思考,我会先找出缺失的 3800 行并尝试查看模式(是因为 user_id 为空还是重复)

SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
MINUS
(SELECT table1.message, table1.sent_date, table1.user_id
FROM (
    SELECT message, sent_date, user_id
    FROM messages
    LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id)

【讨论】:

  • 我看不出任何规律。最终结果中有重复的user_id,因为一个用户可以发送多条消息,还有NULL user_id,对我来说似乎很随机。该查询似乎在没有任何特殊原因的情况下包含或排除结果。
【解决方案4】:

试试这个,我认为这是user_id 的范围问题。

SELECT table1.message, table1.sent_date 
FROM ( 
    SELECT messages.message, messages.sent_date, numbers.user_id 
    FROM messages 
    LEFT JOIN numbers ON messages.from_id = numbers.id 
) AS table1 
LEFT JOIN users ON table1.user_id = users.id 

我不确定user_id 是在messages 还是numbers

【讨论】:

  • 它也只返回 200 个结果。
【解决方案5】:

这不可能发生。

试试这个变体:

SELECT 
    m.message, m.sent_date, n.user_id
FROM
    messages m
  LEFT JOIN 
    numbers AS n  ON m.from_id = n.id
  LEFT JOIN 
    users AS u  ON n.user_id = u.id ;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-16
    • 2013-10-07
    • 1970-01-01
    • 2019-08-15
    • 2017-04-09
    • 1970-01-01
    • 2020-11-03
    • 1970-01-01
    相关资源
    最近更新 更多