2010-01-20 4 views
1

J'ai la requête/MySQL sous-requête suivante:MySQL Sous-requête renvoyant un résultat incorrect?

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 

Compte tenu des critères énumérés, le résultat de la sous-requête (old_id) doit être nul (aucune correspondance ne se trouve dans ma table). Au lieu que MySQL retourne null, il semble juste de supprimer la clause "WHERE ParentUsersValue.user_id = UsersValue.user_id" et de choisir la première valeur qui correspond aux deux autres champs. Est-ce un bug MySQL, ou est-ce pour une raison quelconque le comportement attendu?

Mise à jour:

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')) 
+0

Si vous remplacez 'id' par' user_id' dans la sous-requête, cela retournera-t-il un mauvais 'user_id' (ni' NULL' ni '9917')? – Quassnoi

+0

Si je remplace id avec id_utilisateur, il me donne un mauvais id_utilisateur (l'id_utilisateur qu'il me donne appartient à l'id incorrect indiqué à l'origine). – Blake

+0

Pourriez-vous s'il vous plaît poster les résultats de 'SHOW CREATE TABLE users_value' et' EXPLAIN SELECT ... 'pour la requête? – Quassnoi

Répondre

0

Cela renvoie des résultats corrects (NULL) pour moi:

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 

Pourriez-vous s'il vous plaît exécuter cette requête:

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

et assurez-vous qu'il renvoie 1?

Notez que votre sous-requête ne filtre pas sur created, de sorte que la sous-requête peut renvoyer des valeurs hors de la plage définie par la requête principale.

Mise à jour:

C'est sans aucun doute un bug dans MySQL. La raison est probablement que le chemin d'accès choisi pour UsersValues est index_intersect.

Ceci sélectionne les plages appropriées à partir des deux index et construit leur intersection.

En raison du bogue, la sous-requête dépendante est évaluée avant la fin de l'intersection, c'est pourquoi vous obtenez les résultats avec le another_id correct mais user_id incorrect.

Pourriez-vous s'il vous plaît vérifier si le problème persiste lorsque vous forcez PRIMARY scan sur le 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 

En outre, pour cette requête, vous devez créer un index composite sur (user_id, another_id, id) plutôt que deux indices distincts sur user_id et another_id .

Créer l'index et réécrivez la requête un peu:

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 

Les user_id DESC, another_id DESC clauses sont logiquement redondantes, mais ils feront l'index à utiliser pour la commande.

+0

La requête de comptage renvoie 1. Je peux toujours publier le SHOW CREATE TABLE si vous le souhaitez, mais je viens de découvrir ce qui l'empêche de fonctionner. Lorsque je supprime l'index sur user_id et another_id, cela fonctionne comme prévu. Lorsque j'ajoute l'index, il renvoie l'identifiant invalide. – Blake

+0

Cela peut être vraiment un bug alors. Pourriez-vous s'il vous plaît poster la version 'MySQL',' SHOW CREATE TABLE' et 'EXPLAIN'? – Quassnoi

+0

'@ Blake': bien sûr, l'alpha est définitivement la cause ici. Pourriez-vous s'il vous plaît poster les résultats de 'EXPLAIN'? Mieux vaut le faire comme une mise à jour de votre message plutôt que comme un commentaire, s'il vous plaît. – Quassnoi

0

Avez-vous essayé l'exécution du sous-requête uniquement pour voir si vous obtenez les bons résultats? Pourriez-vous nous montrer le schéma de votre table users_values?

Aussi, essayez de remplacer votre SELECT id dans votre sous-requête par SELECT ParentUsersValue.id

+0

Lorsque j'exécute la sous-requête uniquement, en remplissant les 3 identifiants appropriés, elle renvoie 0 lignes (comme il se doit). En mettant ParentUsersValue.id fait aucune différence. Le schéma est: id est int (11), numéro automatique, non nullable. User_id et another_id sont tous les deux int (11), null par défaut. myvalue est un double, par défaut null. créés et modifiés sont tous les deux datetime, par défaut null. – Blake

Questions connexes