2010-10-06 6 views
2

Salut tout le monde. Je crois que c'est une question «plus grand par groupe» mais même après avoir regardé plusieurs questions sur StackOverflow, je ne sais pas comment l'appliquer à ma situation ...Problème MySQL le plus important par groupe

J'utilise une base de données MySQL et un système de type blog de base mis en place sur les applications informatiques ... les tables ressembler à ceci:

POSTS 
post_id 
post_created 
post_type  -- could be article, review, feature, whatever 
post_status -- 'a' approved or 'd' for draft 

APPS 
app_id 
app_name 
app_platform -- Windows, linux, unix, etc.. 

APP_TO_POST -- links my posts to its relevant application 
atp_id 
atp_app_id 
atp_post_id 

J'utilise la requête de base suivante pour tirer tous les articles pour l'application avec le nom « Photoshop » lorsque le type de publication est un "article" et que le statut de l'article est "a" pour approbation:

SELECT apps.app_name, apps.app_platform, posts.post_created, posts.post_id 
FROM apps 
JOIN app_to_post ON app_to_post.atp_app_id = apps.app_id 
JOIN posts ON app_to_post.atp_post_id = posts.post_id 
WHERE apps.app_name = 'Photoshop' 
AND 
posts.post_type = 'Article' 
AND 
posts.post_status = 'a' 

Ce qui me obtient ces résultats attendus:

app_name app_platform post_created  post_id 
Photoshop Windows  Oct. 20th, 2009 1 
Photoshop Windows  Dec. 1, 2009  3 
Photoshop Macintosh  Nov. 10th, 2009 2 

Est-ce que quelqu'un en mesure de donner un coup de main sur la façon dont je pourrais modifier cette requête seulement tirer l'article le plus récent par plate-forme d'application? Ainsi, par exemple, je voudrais que mes résultats pour ressembler à ceci:

app_name app_platform post_created  post_id 
Photoshop Windows  Dec. 1, 2009  3 
Photoshop Macintosh  Nov. 10th, 2009 2 

sans indiquer l'un des articles 'Photoshop Windows' parce que ce n'est pas la plus récente.

Si je colle simplement sur un MAX(post_created) et un GROUP BY app_platform mes résultats ne se regroupent pas toujours correctement. De la façon dont je le comprends, je dois effectuer une sorte de jointure interne d'une sous-requête?

Répondre

0

Vous êtes sur la bonne voie.

Essayez d'ajouter

group by app_name,app_platform 
having post_created=max(post_created) 

Ou si votre post_id est séquentielle où une valeur plus élevée reflète toujours un poste plus tard, utiliser cette clause ayant: having post_id=max(post_id)

+1

Je suis confronté à un problème similaire, et une instruction "having" ne le résout pas. MySQL semble rapporter le premier résultat qu'il trouve pour chaque ligne groupée, et tout ce que "having" fera est d'exclure complètement toutes les lignes pour lesquelles le premier résultat ne correspond pas au résultat maximum. –

4

Puisque vous avez beaucoup de JOIN s, je suggère la création d'un VIEW premier:

CREATE VIEW articles AS 
    SELECT a.app_name, a.app_platform, p.post_created, p.post_id 
    FROM  apps a 
    JOIN  app_to_post ap ON ap.atp_app_id = a.app_id 
    JOIN  posts p ON ap.atp_post_id = p.post_id 
    WHERE  p.post_type = 'Article' AND p.post_status = 'a'; 

Ensuite, vous pouvez utiliser un NULL-autojointure:

SELECT  a1.app_name, a1.app_platform, a1.post_created, a1.post_id 
FROM  articles a1 
LEFT JOIN articles a2 ON 
      a2.app_platform = a1.app_platform AND a2.post_created > a1.post_created 
WHERE  a2.post_id IS NULL; 

cas de test:

CREATE TABLE posts (
    post_id   int, 
    post_created  datetime, 
    post_type  varchar(30), 
    post_status  char(1) 
); 

CREATE TABLE apps (
    app_id   int, 
    app_name   varchar(40), 
    app_platform  varchar(40) 
); 

CREATE TABLE app_to_post (
    atp_id   int, 
    atp_app_id  int, 
    atp_post_id  int 
); 

INSERT INTO posts VALUES (1, '2010-10-06 05:00:00', 'Article', 'a'); 
INSERT INTO posts VALUES (2, '2010-10-06 06:00:00', 'Article', 'a'); 
INSERT INTO posts VALUES (3, '2010-10-06 07:00:00', 'Article', 'a'); 
INSERT INTO posts VALUES (4, '2010-10-06 08:00:00', 'Article', 'a'); 
INSERT INTO posts VALUES (5, '2010-10-06 09:00:00', 'Article', 'a'); 

INSERT INTO apps VALUES (1, 'Photoshop', 'Windows'); 
INSERT INTO apps VALUES (2, 'Photoshop', 'Macintosh'); 

INSERT INTO app_to_post VALUES (1, 1, 1); 
INSERT INTO app_to_post VALUES (1, 1, 2); 
INSERT INTO app_to_post VALUES (1, 2, 3); 
INSERT INTO app_to_post VALUES (1, 2, 4); 
INSERT INTO app_to_post VALUES (1, 1, 5); 

Résultat:

+-----------+--------------+---------------------+---------+ 
| app_name | app_platform | post_created  | post_id | 
+-----------+--------------+---------------------+---------+ 
| Photoshop | Macintosh | 2010-10-06 08:00:00 |  4 | 
| Photoshop | Windows  | 2010-10-06 09:00:00 |  5 | 
+-----------+--------------+---------------------+---------+ 
2 rows in set (0.00 sec) 

Comme un côté note, en général, vous n'avez pas besoin surrogate key pour votre junction table.Vous pourriez aussi bien mettre en place une clé primaire composite (et idéalement clés étrangères aux tables référencées):

CREATE TABLE app_to_post (
    atp_app_id  int, 
    atp_post_id  int, 
    PRIMARY KEY (atp_app_id, atp_post_id), 
    FOREIGN KEY (atp_app_id) REFERENCES apps (app_id), 
    FOREIGN KEY (atp_post_id) REFERENCES posts (post_id) 
) ENGINE=INNODB; 
+0

Cette jointure null a travaillé pour résoudre un problème similaire pour moi. –

+1

Est-ce une requête efficace? Vous joignez tout à des articles et vous vous joignez à tout avec soi-même. Cela me semble cher. – marc40000

2

Considérons tout d'abord comment obtenir des lignes avec une valeur maximale de votre résultat de la requête et le résultat souhaitable:

Votre résultat: (appelons-le tableau T)

app_name app_platform post_created  post_id 
Photoshop Windows  Oct. 20th, 2009 1 
Photoshop Windows  Dec. 1, 2009  3 
Photoshop Macintosh  Nov. 10th, 2009 2 

Le résultat que vous voulez:

app_name app_platform post_created  post_id 
Photoshop Windows  Dec. 1, 2009  3 
Photoshop Macintosh  Nov. 10th, 2009 2 

Pour obtenir le résultat, vous devez:

  1. Calculer la post_id maximale pour chaque plate-forme pour la table T.
  2. inscription maximum de résultat avec le T table d'origine pour obtenir des valeurs dans d'autres colonnes de la ligne.

la requête est ci-dessous:

SELECT 
    t1.app_name,t1.app_platform,t1.post_created,t1.post_id 
FROM 
    (SELECT app_platform, MAX(post_created) As MaxPostCreated 
    FROM T 
    GROUP BY app_platform) AS t2 JOIN 
    T AS t1 
WHERE 
    t1.app_platform = t2.app_platform1 
    AND t2.MaxPostCreated = t1.post_created 

Dans cette requête, le sous-requête effectuée la première étape, et joindre effectue la seconde étape.

Le résultat final combinant avec votre réponse partielle est illustré ci-dessous (en vue):

CREATE VIEW T 
    SELECT a.app_name, a.app_platform, p.post_created, p.post_id 
    FROM  apps a 
    JOIN  app_to_post ap ON ap.atp_app_id = a.app_id 
    JOIN  posts p ON ap.atp_post_id = p.post_id 
    WHERE  p.post_type = 'Article' AND p.post_status = 'a'; 

SELECT 
    t1.app_name,t1.app_platform,t1.post_created,t1.post_id 
FROM 
    (SELECT app_platform, MAX(post_created) As MaxPostCreated 
    FROM T 
    GROUP BY app_platform) AS t2 JOIN 
    T AS t1 
WHERE 
    t1.app_platform = t2.app_platform1 
    AND t2.MaxPostCreated= t1.post_created 

Par ailleurs, notre équipe est en fait en train de développer un outil en essayant d'aider automatiquement aux utilisateurs d'écrire des requêtes et les utilisateurs peuvent fournir des exemples d'entrées-sorties à l'outil, et l'outil produira une requête. (La première partie de la requête est en fait générée par l'outil! Le lien vers notre prototype est https://github.com/Mestway/Scythe)

J'espère que cela peut vous aider. :)