une seule requête au lieu de deux... [SQL] - SQL/NoSQL - Programmation
Marsh Posté le 24-06-2008 à 10:40:21
Code :
|
Marsh Posté le 24-06-2008 à 10:50:05
Code :
|
C'est presque ça, sauf que je veux uniquement le dernier enregistrement pour chaque MSG.
Or, à cause du USED qui varie tout le temps, on ne peut pas utiliser le DISTINCT.
Marsh Posté le 24-06-2008 à 11:14:06
http://dev.mysql.com/doc/refman/5. [...] p-row.html
si on part du principe que ton dernier enregistrement c'est TIMESTAMP, alors la logique mysql te donnerait ca :
Code :
|
j'aime pas trop mais bon si c'est ce qu'ils préconisent...
Marsh Posté le 24-06-2008 à 11:27:22
Et bien merci, ça a l'air de fonctionner
Même si je suis pas fan de ce genre de requêtes, au moins ça marche.
Marsh Posté le 01-07-2008 à 18:59:45
bah même si t'es pas fan, c'est la seule solution standard qui existe
Marsh Posté le 02-07-2008 à 09:39:38
Je suis pas fan, car celà implique l'utilisation d'un subselect,
or je ne suis pas assuré que le "produit" soit installé systématiquement sur une version MySQL supérieure à la 4.1
Marsh Posté le 02-07-2008 à 13:13:50
Effectivement, j'ai simulé 50000 enregistrements dans ma table principale,
et je kill le process mysql au bout de 2 min de dépit
lorsque la requete est faite avec une sous requete.
Alors qu'avec la méthode du substring, ça ne met que 2 sec
Marsh Posté le 02-07-2008 à 15:52:10
Premièrement, voici les deux tables de test : http://pastebin.com/f23086f34
Et voici le script pour remplir les deux tables : http://pastebin.com/f708b2df3
Les résultats (query_cache=0 sur MySQL 4.1.20) pour (seulement) 5000 lignes dans la table log :
Code :
|
10 rows in set (1 min 31.95 sec)
Code :
|
10 rows in set (1 min 49.53 sec)
Code :
|
10 rows in set (7.20 sec)
Code :
|
10 rows in set (0.19 sec)
La même sur 50000 lignes dans la table log :
10 rows in set (1.83 sec)
Mon choix est fait :-)
Marsh Posté le 02-07-2008 à 16:13:47
si c'estaussi lent avec la sous-requête, c'est qu'il y a un problème dans ta requête ou tes index. normalement le plan d'exécution ne tiens pas compte de la sous-requête
genre, y'a un index (msg_id, time) dans ta table ?
bon, ceci dit, c'est une 4.1.2... vu que ça venait d'être implémenté, ils n'avaient peut-être pas encore fait les optimisations. mysql faut l'utiliser à partir de la 5 quand on fait du SQL "standard". avant, effectivement sorti des bidouilles y'a pas trop moyen de faire des requêtes correctes
Marsh Posté le 02-07-2008 à 16:29:45
Les tables sont crées telle que décrit dans le script, donc pas d'index.
Et les résultats sont les mêmes sur une MySQL 5.0.51b
-- Edit --
Code :
|
Code :
|
C'est plutôt évident avec un EXPLAIN
Marsh Posté le 02-07-2008 à 17:21:14
c'est surtout évident que MySQL c'est de la merde
Création du jeu de test :
Code :
|
Test :
Code :
|
Sortie :
|
Sans index sur mon champ ts.
Sâchant qu'en plus j'ai un grand nombrede doublons (d'où les 48 lignes retournées, ce qui est normalement impossible, mais là ça vient de la création des lignes qui est trop rapide -avoir deux enregistrement dans la base à la même milliseconde, c'est pas de chance -)
Donc 76 ms pour retrouver 48 lignes sans passer par un index parmi 50000, c'est pas mal... Le plan d'exécution (bien plus détaillé que celui de MySQL) n'indique pas de subquery mais que des jointures régulières
SQL Server 2005 Express sur mon portable tout pourri qui swap à mort comme un con.
Marsh Posté le 02-07-2008 à 17:32:20
En modifiant la requête (je prends id et non ts pour ne plus avoir de doublons) et en créant un index :
Code :
|
|
(dans ton cas, l'index c'est sur timestamp, msg_id qu'il faut le créer)
impossible sous sql server de tester ta bidouille sans la sous-requête, mais voici une autre méthode ne nécessitant pas de sous-requête :
Code :
|
|
On voit qu'on rajoute une étape au MÊME plan d'exécution, du coup ça relenti tout
Bref : la méthode avec sous-requête est bien plus performante.
Marsh Posté le 02-07-2008 à 17:36:17
Pourquoi détourner le problème vers SQL Server 2005 Express ?
On m'impose MySQL, on m'impose d'être rétro-compatible (minimum 4.0),
et je ne peux pas toucher à la structure de la table (la création d'index est exclue)
je n'ai ABSOLUMENT aucun intérêt à voir ce que donne la requête sur un autre gestionnaire de BDD ...
Ce que mon post précédent voulait dire c'est que pour certaines requêtes,
même si la solution "parait" évidente (l'utilisation de la sous-requête)
il vaut mieux tester dans un environnement "réel" ou s'y rapprochant le plus possible.
Marsh Posté le 02-07-2008 à 17:44:39
c'est juste que moi je dis que je jette l'éponge quand il s'agit d'écrire une requête pour mysql.
sont pas foutus d'avoir un moteur de requête qui se comporte comme escompté... c'est limite pire qu'Oracle...
Marsh Posté le 02-07-2008 à 17:46:11
ps: et ne pas créer d'index sur ce genre de table, c'est du suicide... une table de log, ça peut rapidement contenir des millions d'enregistrement, mais leur ajout n'est pas suffisant pour pouvoir ralentir le système si la table est indexée. bref, tu vas effondrer le serveur lors des requêtes de selection, sans pour autant ne rien gagner sur l'exploitation générale
Marsh Posté le 02-07-2008 à 17:48:15
teste quand même ma solution sans sous-requête.
elle pourrait être performante, et au moins est standard...
bon, elle est performante dans une certaine mesure, vu qu'elle fait un produit cartésien sur log...
tout dépend ensuite de la façon donc mysql gère la clause having
Marsh Posté le 02-07-2008 à 17:54:16
je refairais ptet les tests avec un index demain, juste pour voir
j'avais pas vu ta requête avec la having (même si elle était dans le post donné par avander)
Marsh Posté le 02-07-2008 à 19:51:10
And the winner is ...
Code :
|
Couplé avec
Code :
|
Requête sur 100.000 enregistrements dans la table `log` en moins de 2 sec.
J'ai pourtant testé avec tout ces index là (certains résultats m'ont paru bizarre quand même)
Et la requête avec un HAVING met 30 sec sur 5000 enregistrements,
ceci, peu importe l'index sur la table `log`.
Marsh Posté le 03-07-2008 à 12:22:40
assez logique que la requête avec le having soit très lente, étant donné qu'elle se paluche un produit cartésien
Marsh Posté le 03-07-2008 à 12:31:40
Mais c'est pas la plus lente
La requête "originale", celle qui me semblait la plus logique,
met entre 1 et 3 fois plus de temps -_-'
La requête en question (avec la sous-requête)
Code :
|
Mais bon, je pense avoir trouvé le bon compromis
Marsh Posté le 03-07-2008 à 12:36:27
normal, mysql gère assez mal les max().
en interne, il devrait aussi bien faire un limit + order by lorsqu'il n'y a pas de membres dans la clause group by
mais c'est clairement une optimisation qui est passée à la trappe !
le plus fun, c'est que la plus rapide et la plus lente sont strictement les mêmes à cette optimisation près ! il semble urgent que mysql pense à mettre en place cette optimisation
Marsh Posté le 24-06-2008 à 10:17:52
Bonjour,
j'ai actuellement deux tables.
Id n'a pas besoin de description il me semble. Timestamp non plus.
Used est un pourcentage qui peut donc varier entre 0 et 100 (oui, je sais, tinyint)
et Msq_id peut être considéré comme une clé étrangère de la table Msg.
Bon bah là, c'est clair, pas besoin d'explications.
Le but est de récupérer le dernier MSG et le USED pour chacun des MSG de la table MSG.
Exemple:
Dans ce cas, il faut que je récupère un truc du genre
Le MSG et non le MSG_ID, ainsi que le dernier USED pour chacun des MSG.
Bien sûr, il faut que cette requête reste opérationnelle lors de l'ajout d'un MSG dans la table MSG (pas de "limit 2" )
Actuellement, je fais ça en deux requêtes, mais vu le contenu important des deux tables, chaque parcours est assez couteux,
donc j'aimerais le faire si possible en une seule requête.