MySQL наибольшая проблема в группе - PullRequest
2 голосов
/ 06 октября 2010

Привет всем. Я считаю, что это вопрос «наибольший n на группу», но даже после просмотра нескольких вопросов по StackOverflow я не уверен, как применить это к моей ситуации ...

Я использую базу данных MySQL и настроил базовую систему типа блога для компьютерных приложений ... Таблицы выглядят так:

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

Я использую следующий базовый запрос для извлечения всех статей для приложения с именем «Photoshop», где тип публикации - «Статья», а статус статьи - «одобрено»:

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'

Что дает мне эти ожидаемые результаты:

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

Сможет ли кто-нибудь помочь мне, как я могу изменить этот запрос, чтобы получить только самую последнюю статью для каждой платформы приложения? Например, я хотел бы, чтобы мои результаты выглядели так:

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

И пропустите одну из 'Photoshop Windows' статей, поскольку она не самая последняя.

Если я просто использую MAX(post_created) и GROUP BY app_platform, мои результаты не всегда группируются правильно. Насколько я понимаю, мне нужно выполнить какое-то внутреннее соединение подзапроса?

Ответы [ 3 ]

4 голосов
/ 06 октября 2010

Поскольку у вас достаточно JOIN с, я предлагаю сначала создать VIEW:

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';

Тогда вы можете использовать NULL-самостоятельное соединение:

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;

Контрольный пример:

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);

Результат:

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

Как примечание: в общем случае вам не нужен суррогатный ключ для вашей соединительной таблицы . Вы также можете настроить составной первичный ключ (и в идеале внешние ключи для ссылочных таблиц):

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;
3 голосов
/ 16 сентября 2016

Давайте сначала рассмотрим, как получить строки с максимальным значением из вашего результата запроса и желаемого результата:

Ваш результат: (назовем это таблицей 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

Результат, который выwant:

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

Чтобы получить результат, вы должны:

  1. Рассчитать максимальный post_id для каждой платформы для таблицы T.
  2. Присоединиться к максимальному результатус исходной таблицей T для получения значений в других столбцах строки.

Запрос ниже:

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

В этом запросе подзапрос выполнил первый шаг, иОбъединение выполняет второй шаг.

Окончательный результат в сочетании с вашим частичным ответом показан ниже (с видом):

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

Кстати, наша команда фактически разрабатывает инструментпытаясь автоматически помочь пользователям писать запросы, и пользователи могут предоставить инструментам примеры ввода-вывода, и инструмент создаст запрос.(Первая часть запроса на самом деле генерируется инструментом! Ссылка на наш прототип: https://github.com/Mestway/Scythe)

Надеюсь, это поможет вам.:)

0 голосов
/ 06 октября 2010

Вы на правильном пути.

Попробуйте добавить

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

Или, если ваш post_id является последовательным, где более высокое значение всегда будет отражать более позднюю публикацию, используйте следующую фразу:having post_id=max(post_id)

...