[résolu] Problème de requête, CPU à 100%...

Problème de requête, CPU à 100%... [résolu] - SQL/NoSQL - Programmation

Marsh Posté le 26-07-2005 à 15:12:51    

Je dois écrire une requête qui me retourne la liste des personnes non connectées depuis 1 mois (à un réseau).
La requête que j'ai écris :

SELECT * FROM historique_login WHERE login_loginname NOT IN (SELECT login_loginname FROM historique_login WHERE login_timestamp > (NOW( ) - INTERVAL 1 MONTH ))


 
Quand je lance la requête dans phpMyAdmin, la page se charge très lentement et se bloque. Je n'arrive pas à afficher de résultats. Le processus mysqld-nt.exe consomme à ce moment 99% du CPU et met longtemps à redescendre, même quand je tente d'arrêter l'exécution de la requête.
 
Qu'est-ce qui cloche dans cette requête ?  :??:


Message édité par RaTo le 27-07-2005 à 11:28:08
Reply

Marsh Posté le 26-07-2005 à 15:12:51   

Reply

Marsh Posté le 26-07-2005 à 15:17:55    

Tu peux pas la faire sans sous-requête?[:mlc]
du style:
select * from historique_login where login_timestamp < (NOW( ) - INTERVAL 1 MONTH )  
 
[:dawa]


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:18:03    

Remplace le NOT IN par un NOT EXISTS pour voir.
 

Code :
  1. SELECT *
  2. FROM historique_login hl
  3. WHERE NOT EXISTS
  4. (SELECT 1
  5. FROM historique_login
  6. WHERE login_loginname = hl.login_loginname
  7. AND login_timestamp > (NOW( ) - INTERVAL 1 MONTH ));

Reply

Marsh Posté le 26-07-2005 à 15:18:33    

Beegee a écrit :

Remplace le NOT IN par un NOT EXISTS pour voir.
 

Code :
  1. SELECT *
  2. FROM historique_login hl
  3. WHERE NOT EXISTS
  4. (SELECT 1
  5. FROM historique_login
  6. WHERE login_loginname = hl.login_loginname
  7. AND login_timestamp > (NOW( ) - INTERVAL 1 MONTH ));



Mais [:ktulu] !


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:19:08    

skeye a écrit :

Tu peux pas la faire sans sous-requête?[:mlc]
du style:
select * from historique_login where login_timestamp < (NOW( ) - INTERVAL 1 MONTH )  
 
[:dawa]


 
Ben non, vu sa demande, il veut pas voir les utilisateurs qui se sont connectés durant le dernier mois ;)

Reply

Marsh Posté le 26-07-2005 à 15:19:47    

Beegee a écrit :

Ben non, vu sa demande, il veut pas voir les utilisateurs qui se sont connectés durant le dernier mois ;)


c'est pas ce que j'ai écrit?


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:20:54    

Non, tu sélectionnes l'historique de plus d'un mois de tous les utilisateurs.
 
Ce n'est pas la même chose que l'historique complet des utilisateurs non connectés durant le dernier mois.

Reply

Marsh Posté le 26-07-2005 à 15:25:11    

Beegee a écrit :

Non, tu sélectionnes l'historique de plus d'un mois de tous les utilisateurs.
 
Ce n'est pas la même chose que l'historique complet des utilisateurs non connectés durant le dernier mois.


 
Exactement.  :jap:  
 
Sinon en remplaçant le NOT IN par NOT EXISTS j'ai une erreur de syntaxe :
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'EXISTS (SELECT login_loginname FROM historique_login WHERE login_timestamp > (NO' at line 1  
 

Reply

Marsh Posté le 26-07-2005 à 15:25:14    

Beegee a écrit :

Non, tu sélectionnes l'historique de plus d'un mois de tous les utilisateurs.
 
Ce n'est pas la même chose que l'historique complet des utilisateurs non connectés durant le dernier mois.


 
ah oui.
Ca empêche pas de le faire sans sous-requête.:o
 
select max(login_timestamp), champ2, champ3
from historique_login
where login_timestamp < (NOW( ) - INTERVAL 1 MONTH )
group by (champ2, champ3)
 
:o
 
[edit]
 
encore raté...[:joce]


Message édité par skeye le 26-07-2005 à 15:25:55

---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:26:29    

bah pourquoi pas ça simplement :
 
SELECT * FROM historique_login WHERE login_timestamp <= (NOW( ) - INTERVAL 1 MONTH ))
 
Si tu supprimes les logins de plus d'un mois alors il suffit de garder ceux de moins d'un mois... comment faire simple quand on peut faire compliqué :/

Reply

Marsh Posté le 26-07-2005 à 15:26:29   

Reply

Marsh Posté le 26-07-2005 à 15:27:17    

RaTo a écrit :

Exactement.  :jap:  
 
Sinon en remplaçant le NOT IN par NOT EXISTS j'ai une erreur de syntaxe :
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'EXISTS (SELECT login_loginname FROM historique_login WHERE login_timestamp > (NO' at line 1


 
essaye d'aliaser ta table dans la requête imbriquée, aussi.


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:27:53    

megadub a écrit :

bah pourquoi pas ça simplement :
 
SELECT * FROM historique_login WHERE login_timestamp <= (NOW( ) - INTERVAL 1 MONTH ))
 
Si tu supprimes les logins de plus d'un mois alors il suffit de garder ceux de moins d'un mois... comment faire simple quand on peut faire compliqué :/


 
Bien vu, même connerie que moi![:joce]
J'avais pas pensé non plus qu'il conservait tout son historique...[:joce]


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:31:57    

par curiosité, ya combien de lignes dans ta table historique_login?
 
[edit]
 
Question subsidiaire : ne serait-ce pas intéressant de la transformer en dernier_login?[:joce]


Message édité par skeye le 26-07-2005 à 15:32:35

---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:32:33    

Vérifie déjà que dans ta version de MySQL y'a bien le EXISTS qui est supporté. Apparement, il ne l'est pas.
 
S'il ne supporte pas, alors change de version (super, j'adore ce SGBD :D) ou tu peux aussi d'apporter une légère optimisation à ton NOT IN en résuidant la liste qu'il retourne :
 

Code :
  1. SELECT * FROM historique_login WHERE login_loginname NOT IN (SELECT login_loginname FROM historique_login WHERE login_timestamp > (NOW( ) - INTERVAL 1 MONTH ) group by login_loginname)


 
C'est pas forcément énorme comme optimisation, mais ça devrait aider.
 
Ensuite, n'oublie pas de créer deux index :
 
historique_login(login_timestamp, login_loginname)
historique_login(login_loginname)
 
Le premier devant pouvoir être unique (en comptant qu'une personne ne se logue normalement pas deux fois dans la même milli-seconde.

Reply

Marsh Posté le 26-07-2005 à 15:32:50    

En balaçant la requête :

Code :
  1. SELECT *
  2. FROM historique_login hl
  3. WHERE NOT EXISTS
  4. (SELECT 1
  5. FROM historique_login
  6. WHERE login_loginname = hl.login_loginname
  7. AND login_timestamp > (NOW( ) - INTERVAL 1 MONTH ));


 
Même résultat qu'avec la mienne, plantage de mysql...

Reply

Marsh Posté le 26-07-2005 à 15:34:09    

Teste ma solution.

Reply

Marsh Posté le 26-07-2005 à 15:34:09    

Autre question qui va avec : y a-t-il un index sur login_loginname ?

Reply

Marsh Posté le 26-07-2005 à 15:34:31    

Avec les index evidement.

Reply

Marsh Posté le 26-07-2005 à 15:36:10    

skeye a écrit :

Bien vu, même connerie que moi![:joce]
J'avais pas pensé non plus qu'il conservait tout son historique...[:joce]


 
ha oui, alors :
 

Code :
  1. SELECT login_name, max(login_timestamp) FROM historique_login WHERE login_timestamp <= (NOW( ) - INTERVAL 1 MONTH ))
  2. GROUP BY login_name


 
ça devrait le faire ça non ?

Reply

Marsh Posté le 26-07-2005 à 15:36:26    

skeye a écrit :

par curiosité, ya combien de lignes dans ta table historique_login?


 
22 116
 

skeye a écrit :

Question subsidiaire : ne serait-ce pas intéressant de la transformer en dernier_login?[:joce]


 
 :??:  

Reply

Marsh Posté le 26-07-2005 à 15:37:17    

Vu que EXISTS ne semble pas supporté, passage par jointure externe, comme d'hab' :)
 

Code :
  1. SELECT *
  2. FROM historique_login hl1
  3. LEFT JOIN  hl2 ON hl2.login_loginname = hl1.login_loginname
  4. WHERE hl2.login_timestamp > (NOW( ) - INTERVAL 1 MONTH)
  5. AND hl2.login_loginname IS NULL;


 
Il suffit d'avoir un index sur login_loginname.
 
edit: si tu veux juste les logins et que tu n'as pas besoin de l'historique, alors tu peux faire ça aussi :
 

Code :
  1. SELECT login_loginname, MAX(login_timestamp)
  2. FROM historique_login
  3. GROUP BY login_loginname
  4. HAVING MAX(login_timestamp) > (NOW( ) - INTERVAL 1 MONTH);


Message édité par Beegee le 26-07-2005 à 15:41:06
Reply

Marsh Posté le 26-07-2005 à 15:37:21    

megadub a écrit :

ha oui, alors :
 

Code :
  1. SELECT login_name, max(login_timestamp) FROM historique_login WHERE login_timestamp <= (NOW( ) - INTERVAL 1 MONTH ))
  2. GROUP BY login_name


 
ça devrait le faire ça non ?


 
c'est le genre de trucs que j'ai proposé aussi...tu vas avoir pour tous les utilisateurs le plus récent login ayant plus d'un mois...


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:37:41    

A mon avis, il faut surtout un group by (pour faire un distinct) dans la sous-requête.
 
En effet, même si ce sont des doublons, un NOT IN pourtant sur des milliers de valeurs, ben ça fait tout tomber. Avec un distinct, y'en aura forcément infiniment moins, et ça devrait grandement améliorer l'éxécution du NOT IN

Reply

Marsh Posté le 26-07-2005 à 15:39:20    


Bah ça a un intérêt de garder l'historique complet de tous les logins de tes utilisateurs, en vrai?
Parce-que si non, tu gardes la date du dernier login pour chaque utilisateur, et basta...ce qui résoud aussi le pb...[:joce]


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:39:51    

Arjuna a écrit :

Vérifie déjà que dans ta version de MySQL y'a bien le EXISTS qui est supporté. Apparement, il ne l'est pas.


 
Je pense que le EXISTS est bien dans la version de MySQL que j'utilise, car j'arrive à faire des requêtes du style "DROP TABLE IF EXISTS..."
 
La version : 4.1.10a

Reply

Marsh Posté le 26-07-2005 à 15:41:42    

Je t'ai donné 2 requêtes qui devraient marcher, juste au-dessus, y a pas besoin de EXISTS de toute façon.

Reply

Marsh Posté le 26-07-2005 à 15:42:43    

skeye a écrit :

Bah ça a un intérêt de garder l'historique complet de tous les logins de tes utilisateurs, en vrai?

Oui. (enfin c'est une exigence de mon supérieur, donc je ne peux pas modifier ça)


Message édité par RaTo le 26-07-2005 à 15:43:33
Reply

Marsh Posté le 26-07-2005 à 15:43:10    

skeye a écrit :

c'est le genre de trucs que j'ai proposé aussi...tu vas avoir pour tous les utilisateurs le plus récent login ayant plus d'un mois...


 
exact ;)
 

Reply

Marsh Posté le 26-07-2005 à 15:43:18    

bon, alors voir plus haut les suggestions de beegee et arjuna...[:skeye]


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:44:37    

Je pense que la bonne piste effectivement c'est de réduire au max le nombre de réponses de la sous-requête...


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:47:23    

Personne veut suivre ma requête :spamafote:

Reply

Marsh Posté le 26-07-2005 à 15:48:20    

C'est mal de tronquer une table de logs.

Reply

Marsh Posté le 26-07-2005 à 15:54:48    

Arjuna a écrit :

Personne veut suivre ma requête :spamafote:


 
J'ai créé les index et balancé ta requête, ça tourne là, ça à l'air de ramer tout autant...
 
Je crois que le problème c'est en fait les 22 000 lignes.

Reply

Marsh Posté le 26-07-2005 à 15:55:50    

RaTo a écrit :

J'ai créé les index et balancé ta requête, ça tourne là, ça à l'air de ramer tout autant...
 
Je crois que le problème c'est en fait les 22 000 lignes.


22000 c'est pas la mer à boire non plus...à moins que le serveur soit vraiment juste ya pas de raison que ce soit si lent!


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 26-07-2005 à 15:59:31    

RaTo a écrit :

J'ai créé les index et balancé ta requête, ça tourne là, ça à l'air de ramer tout autant...

ça tourne toujours..


Message édité par RaTo le 26-07-2005 à 16:00:08
Reply

Marsh Posté le 26-07-2005 à 16:06:50    

Bon j'ai arrêté la requête d'Arjuna, ça ramait trop..

Reply

Marsh Posté le 26-07-2005 à 16:42:38    

Et mes requêtes alors ? :D

Reply

Marsh Posté le 26-07-2005 à 18:37:25    

C'est étrange que ça ramme autant, t'as combien de login différents ?
 
Parcequ'un IN, c'est lent, certes, mais à la base, si le nombre est limité, ça doit quand même pas prendre des siècles... Idem pour un group by sur 22000 lignes, ça représente pas un gros volume.
 
Une requête normale fonctionne ?

Reply

Marsh Posté le 26-07-2005 à 19:52:10    

Bonsoir les amis,
 
Je vous réponds juste, je continuerai à tester demain au boulot, là je ne peux pas, merci de votre aide.

Reply

Marsh Posté le 27-07-2005 à 09:36:32    

Arjuna a écrit :

C'est étrange que ça ramme autant, t'as combien de login différents ?


582.

Arjuna a écrit :


Une requête normale fonctionne ?


Oui.

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed