Précédent   Forum des professionnels en informatique > Bases de données > MySQL > Requêtes
Requêtes Forum d'entraide sur les requêtes MySQL
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 06/12/2010, 22h50   #1
Nouveau Membre du Club
 
Inscription : février 2010
Messages : 117
Détails du profil
Informations forums :
Inscription : février 2010
Messages : 117
Points : 33
Points : 33
Par défaut Conditionner une "limit"

Bonjour,

Vu que je suis incapable d'exprimer simplement mon problème avec des mots je vais aller droit au but :

Je travaille sur la vue suivante qui varie quotidiennement:
Code :
1
2
3
4
5
6
 > SELECT * FROM top_activites;
id | score
86 | 7899
168 | 4689
78 | 3859
...
Je dois fournir (dans une vue) en fonction de chaque jour du mois les 5ènieme meilleures activités :
Soit N le jour du mois : je dois fournir les activités entre 5 * (N - 1) et 4 + 5 * (N - 1)

En gros la requête devrait avoir cette tronche :
Code :
1
2
3
SELECT *
FROM top_activites
LIMIT 5 * (dayofmonth(current_date) - 1), 4 + 5 * (dayofmonth(current_date) - 1)
Problème la doc mysql indique que limit ne marche qu'avec des constantes ....

J'ai donc pensé à deux choses.
La première c'est donc de simuler le comportement de limite avec un encadrement du score , donc si on est le premier jour du mois il faut prendre tous les tuples dont le score est entre le 1er et le 4 meilleur score, le deuxième jour entre le 5e et le 9e etc etc , comme suit :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
SELECT *
FROM top_activites
WHERE score <= (SELECT CASE
                           WHEN dayofmonth(current_date) = 1
                           THEN (SELECT score 
                                     FROM top_activites
                                     LIMIT 0,1)
                           WHEN dayofmonth(current_date) = 2
                           THEN (SELECT score 
                                     FROM top_activites
                                     LIMIT 5,1)
                          ... ON continue 31 fois
AND score >=     (SELECT CASE
                            WHEN dayofmonth(current_date) = 1
                           THEN (SELECT score 
                                     FROM top_activites
                                     LIMIT 4,1)
                          WHEN dayofmonth(current_date) = 2
                           THEN (SELECT score 
                                     FROM top_activites
                                     LIMIT 9,1)
etc etc
Déjà ça marche à moitié si on a des scores égaux ...
Et franchement ça m'a fait mal au coeur de faire ça mais c'est la seul solution viable que j'ai pu trouver ....

Ma deuxième idée est plus simple mais je perds le dynamisme de ma vue :
Je dois donc exécuter ce code tous les jours ... :
Code :
1
2
3
4
5
6
7
8
9
 
DROP TABLE IF EXISTS top_tmp
CREATE TABLE top_tmp(id int UNSIGNED AUTO_INCREMENT, id_activite int UNSIGNED, score int UNSIGNED);
INSERT IGNORE INTO top_tmp (id_activite, score) SELECT * FROM top_activites;
CREATE VIEW static_fail AS
  SELECT id_activite, score 
  FROM top_tmp
  WHERE id >= 5 * (dayofmonth(current_date) - 1)
  AND id <= 5 * (dayofmonth(current_date) - 1) + 4
Je pensais au début utiliser un "id de vue" mais seul oracle en dispose ...
Malgré qu'on peut le simuler comme suit, je n'ai pas réussi à utiliser une colonne dépendant d'une variable
Code :
1
2
3
4
5
6
 
          SELECT id_activite, 
                       score,
                       (@i := @i + 1) AS rang
          FROM top_activites, 
                    SELECT (@i := 0) AS t
Quoi que je fasse rang est inutilisable dans mes clause where :/


Une autre idée lu sur un post mysql serait de faire une grande table avec des id bigint unsigned de 1 à l'infinie et donc de faire une jointure entre ma vue (classée sur le score) et cette table pour ajouter donc un "id auto increment" sur la vue ... mais après avoir testé tous les types de jointure j'ai pas réussi



Dans tous les cas j'ai l'impression de faire des trucs vraiment pas terrible ...
C'est pourquoi je poste ici afin de savoir si quelqu'un à déjà rencontré un problème similaire et aurait trouvé une meilleure solution !

Je suis prêt à fournir d'avantage de précisions si nécessaire.
En vous remerciant d'avance.
Cdt.
ithurts est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2010, 01h35   #2
Membre Expert
 
Inscription : août 2008
Messages : 1 271
Détails du profil
Informations forums :
Inscription : août 2008
Messages : 1 271
Points : 1 929
Points : 1 929
Quelque chose me gêne dans ton approche "basic" :
Code :
1
2
3
SELECT *
FROM top_activites
LIMIT 5 * (dayofmonth(current_date) - 1), 4 + 5 * (dayofmonth(current_date) - 1)
Comment associes tu une date avec tes données id, score. J'ai bien vu que c'éatit lié au score mais je n'ai pas bien compris.
De plus un LIMIT doit être associé à un ORDER BY pour être pérenne.
Code :
1
2
3
4
5
6
7
SELECT * 
FROM (SELECT id_activite, 
		score,
		(@i := @i + 1) AS rang
	FROM top_activites
	) AS t
WHERE rang = 10
J'imagine que tu as testé mais ton exemple n'est pas probant.
Evidemment il faudra rajouter un ORDER BY et potentiellemnt passer par une sous-sous-requête cf d'abord trier puis incrémenter rang puis filter sur rang.
Je n'ai pas tester n'ayant pas MySql sous la main.

Sinon tu peux générer une table calendrier et créer une view sur la jointure entre la table calendrier et la vue top_activites mais comme je n'ai pas bien compris les critères de jointures je ne m'y risquerais pas
Avoir une table calendrier est très commun quand on doit fournir des stats par date.
Une petite procédure MySql fera l'affaire pour alimenter la table calendrier.
De plus une table calendrier te sera probablement utile pour d'autres requêtes.
skuatamad est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2010, 08h00   #3
Nouveau Membre du Club
 
Inscription : février 2010
Messages : 117
Détails du profil
Informations forums :
Inscription : février 2010
Messages : 117
Points : 33
Points : 33
Bonjour et merci pour votre réponse !

Citation:
Comment associes tu une date avec tes données id, score. J'ai bien vu que c'éatit lié au score mais je n'ai pas bien compris.
De plus un LIMIT doit être associé à un ORDER BY pour être pérenne.
Je suis allé trop vite et j'ai oublié de précisé que la vue top_activites était nativement classé par ordre décroissant sur le score.
Le score indiquant évidemment la popularités d'une activité

Mon objectif est donc de le 1er jour de chaque mois de fournir les 5 meilleures activités de cette vue, puis le 2e jour les 5 suivantes (5 à 9) , puis le 3e jour les 5 suivantes (10 à 14), puis le 4e jour les 5 suivantes .. ... puis le Nieme jour les activités entre 5 * (N - 1) et 4 + 5 * (N - 1)

En ce qui concerne la requête avec variable :
Code :
1
2
mysql> SELECT *  FROM (SELECT id,   score, (@i := @i + 1) AS rang FROM top_activites ) AS t WHERE rang = 10;
ERROR 1054 (42S22): Unknown COLUMN 'score' IN 'field list'
Citation:
Sinon tu peux générer une table calendrier et créer une view sur la jointure entre la table calendrier et la vue top_activites mais comme je n'ai pas bien compris les critères de jointures je ne m'y risquerais pas
Justement c'est mon problème, il n'y aucun lien entre mes colonnes ID et score et la date en cours, impossible de faire de jointure.
Ma seule relation serait le n° de row d'un tuple "id | score" avec la date du mois en cours, chose que j'essaie désespérément de rajouter sans succès ..


Cdt.
ithurts est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2010, 16h51   #4
Nouveau Membre du Club
 
Inscription : février 2010
Messages : 117
Détails du profil
Informations forums :
Inscription : février 2010
Messages : 117
Points : 33
Points : 33
Mea culpa je me suis trompé hier soir (c'est ça de bosser cher soi !) en effet ça marche en faisant ce qui suit

Code :
1
2
3
4
5
6
7
8
SELECT * 
FROM 	(SELECT	id, 
		count, 
		(@i := @i + 1) AS rank 
	FROM 	top_activites, 
		(SELECT @i := 0) AS tmp1) AS tmp2
WHERE rank >= 5 * (dayofmonth(current_date) - 1)
AND rank <= 4 + 5 * (dayofmonth(current_date) - 1);
Je peux pas faire de vue mais je m'en fous, c'est tellement élégant que j'arriverais à l'adapter

Merci infiniment skuatamad ! 2 jours que je m'acharne dessus !
ithurts est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 18h59.


 
 
 
 
Partenaires

Hébergement Web