【问题标题】:MySQL Subquery returning incorrect result?MySQL子查询返回不正确的结果?
【发布时间】:2010-01-20 14:32:37
【问题描述】:

我有以下 MySQL 查询/子查询:

SELECT id, user_id, another_id, myvalue, created, modified,
(
    SELECT id 
    FROM users_values AS ParentUsersValue
    WHERE ParentUsersValue.user_id = UsersValue.user_id
    AND ParentUsersValue.another_id = UsersValue.another_id 
    AND ParentUsersValue.id < UsersValue.id 
    ORDER BY id DESC 
    LIMIT 1
) AS old_id

FROM users_values AS UsersValue
WHERE created >= '2009-12-20' 
AND created <= '2010-01-21' 
AND user_id = 9917
AND another_id = 23

鉴于列出的条件,子查询 (old_id) 的结果应该为 null(在我的表中找不到匹配项)。而不是 MySQL 返回 null,它似乎只是删除了“WHERE ParentUsersValue.user_id = UsersValue.user_id”子句并选择与其他两个字段匹配的第一个值。这是一个 MySQL 错误,还是出于某种原因这是预期的行为?

更新:

CREATE TABLE users_values (
    id int(11) NOT NULL AUTO_INCREMENT,
    user_id int(11) DEFAULT NULL,
    another_id int(11) DEFAULT NULL,
    myvalue double DEFAULT NULL,
    created datetime DEFAULT NULL,
    modified datetime DEFAULT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2801 DEFAULT CHARSET=latin1

EXPLAIN EXTENDED:

id  select_type table   type    possible_keys   key key_len ref rows    filtered    Extra
1   PRIMARY UsersValue  index_merge user_id,another_id  user_id,another_id  5,5 NULL    1   100.00  Using intersect(user_id,another_id); Using where
2   DEPENDENT SUBQUERY  ParentUsersValue    index   PRIMARY,user_id,another_id  PRIMARY 4   NULL    1   100.00  Using where

EXPLAIN EXTENDED Warning 1003:

select `mydb`.`UsersValue`.`id` AS `id`,`mydb`.`UsersValue`.`user_id` AS `user_id`,`mydb`.`UsersValue`.`another_id` AS `another_id`,`mydb`.`UsersValue`.`myvalue` AS `myvalue`,`mydb`.`UsersValue`.`created` AS `created`,`mydb`.`UsersValue`.`modified` AS `modified`,(select `mydb`.`ParentUsersValue`.`id` AS `id` from `mydb`.`users_values` `ParentUsersValue` where ((`mydb`.`ParentUsersValue`.`user_id` = `mydb`.`UsersValue`.`user_id`) and (`mydb`.`ParentUsersValue`.`another_id` = `mydb`.`UsersValue`.`another_id`) and (`mydb`.`ParentUsersValue`.`id` < `mydb`.`UsersValue`.`id`)) order by `mydb`.`ParentUsersValue`.`id` desc limit 1) AS `old_id` from `mydb`.`users_values` `UsersValue` where ((`mydb`.`UsersValue`.`another_id` = 23) and (`mydb`.`UsersValue`.`user_id` = 9917) and (`mydb`.`UsersValue`.`created` >= '2009-12-20') and (`mydb`.`UsersValue`.`created` <= '2010-01-21'))

【问题讨论】:

  • 如果在子查询中将id替换为user_id,会不会返回错误的user_idNULL9917都不是)?
  • 如果我将 id 替换为 user_id,它会给我一个错误的 user_id(它给我的 user_id 属于最初列出的错误 id)。
  • 能否请您发布SHOW CREATE TABLE users_valueEXPLAIN SELECT ... 的查询结果?

标签: sql mysql subquery


【解决方案1】:

这将为我返回正确的结果 (NULL):

CREATE TABLE users_values (id INT NOT NULL PRIMARY KEY, user_id INT NOT NULL, another_id INT NOT NULL, created DATETIME NOT NULL);

INSERT
INTO    users_values VALUES (1, 9917, 23, '2010-01-01');

SELECT  *,
        (
        SELECT  id
        FROM    users_values AS ParentUsersValue
        WHERE   ParentUsersValue.user_id = UsersValue.user_id
                AND ParentUsersValue.another_id = UsersValue.another_id
                AND ParentUsersValue.id < UsersValue.id
        ORDER BY id
                DESC
        LIMIT 1
        ) AS old_id
FROM    users_values AS UsersValue
WHERE   created >= '2009-12-20'
        AND created <= '2010-01-21'
        AND user_id = 9917
        AND another_id = 23

能否请您运行此查询:

SELECT  COUNT(*)
FROM    users_values AS UsersValue
WHERE   user_id = 9917
        AND another_id = 23

并确保它返回1?

请注意,您的子查询不会过滤 created,因此子查询可能会返回超出主查询定义范围的值。

更新:

这绝对是MySQL 中的一个错误。

最可能的原因是为UsersValues选择的访问路径是index_intersect

这会从两个索引中选择适当的范围并构建它们的交集。

由于该错误,依赖子查询在交集完成之前被评估,这就是为什么您会得到正确的another_id 但错误的user_id 的结果。

您能否在对UsersValues 强制进行PRIMARY 扫描时检查问题是否仍然存在:

SELECT  *,
        (
        SELECT  id
        FROM    users_values AS ParentUsersValue
        WHERE   ParentUsersValue.user_id = UsersValue.user_id
                AND ParentUsersValue.another_id = UsersValue.another_id
                AND ParentUsersValue.id < UsersValue.id
        ORDER BY id
                DESC
        LIMIT 1
        ) AS old_id
FROM    users_values AS UsersValue FORCE INDEX (PRIMARY)
WHERE   created >= '2009-12-20'
        AND created <= '2010-01-21'
        AND user_id = 9917
        AND another_id = 23

此外,对于此查询,您应该在 (user_id, another_id, id) 上创建一个复合索引,而不是在 user_idanother_id 上创建两个不同的索引。

创建索引并稍微重写查询:

SELECT  *,
        (
        SELECT  id
        FROM    users_values AS ParentUsersValue
        WHERE   ParentUsersValue.user_id = UsersValue.user_id
                AND ParentUsersValue.another_id = UsersValue.another_id
                AND ParentUsersValue.id < UsersValue.id
        ORDER BY
                user_id DESC, another_id DESC, id DESC
        LIMIT 1
        ) AS old_id
FROM    users_values AS UsersValue
WHERE   created >= '2009-12-20'
        AND created <= '2010-01-21'
        AND user_id = 9917
        AND another_id = 23

user_id DESC, another_id DESC 子句在逻辑上是多余的,但它们会使索引用于排序。

【讨论】:

  • count 查询确实返回 1。如果您愿意,我仍然可以发布 SHOW CREATE TABLE,但我刚刚发现了导致它停止工作的原因。当我删除 user_id 和 another_id 上的索引时,它按预期工作。当我重新添加索引时,它会返回无效的 id。
  • 那么这可能是一个真正的错误。能否请您发布MySQL 版本、SHOW CREATE TABLEEXPLAIN
  • @Blake: 当然,alpha 绝对是这里的原因。能否请您发布EXPLAIN 的结果?如果您愿意,最好将其作为您帖子的更新而不是作为评论。
  • 还有一件事:您能否运行EXPLAIN EXTENDED 并发布它产生的警告消息(带有代码1003 的警告消息)?请将其发布为您问题的更新。
  • 刚刚更新了问题。我发布的第一个 EXPLAIN 和警告 1003 来自没有索引的表,但现在它来自有索引的表。
【解决方案2】:

您是否尝试运行子查询只是为了查看您是否获得了正确的结果?您能否向我们展示您的 users_values 表的架构?

另外,尝试将子查询中的 SELECT id 替换为 SELECT ParentUsersValue.id

【讨论】:

  • 当我只运行子查询并填写 3 个适当的 id 时,它返回 0 行(应该如此)。放置 ParentUsersValue.id 没有任何区别。架构是:id 是 int(11),自动编号,不可为空。 user_id 和 another_id 都是 int(11),默认为 null。 myvalue 是一个双精度值,默认为 null。 created 和 modified 都是日期时间,默认为空。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多