MySQL Subquery возвращает неверный результат? - PullRequest
1 голос
/ 20 января 2010

У меня есть следующий запрос / подзапрос 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) должен быть нулевым (в моей таблице совпадений не найдено). Вместо того, чтобы 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'))

Ответы [ 2 ]

0 голосов
/ 20 января 2010

Это возвращает правильные результаты (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.

Не могли бы вы проверить, сохраняется ли проблема при принудительном сканировании PRIMARY на UsersValues:

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_id и another_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 логически избыточны, но они сделают индекс для использования при заказе.

0 голосов
/ 20 января 2010

Вы пытались запустить подзапрос только для проверки правильности результатов? Не могли бы вы показать нам схему для вашей users_values таблицы?

Также попробуйте заменить SELECT id в подзапросе на SELECT ParentUsersValue.id

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...