2013-08-14 3 views
0

В настоящее время я борюсь с запросом и нуждаюсь в некоторой помощи.Выбор значений из столбца, определенного агрегированной функцией

У меня есть две таблицы:

messages { 
    ts_send, 
    message, 
    conversations_id 
} 

conversations { 
    id 
} 

Я хочу, чтобы выбрать сообщения, имеющие последнюю ts_send от каждого разговора. Итак, если у меня будет 3 беседы, я получаю 3 сообщения.

Я начал писать следующий запрос, но я смутился, как я должен сравнивать max (ts_send) для каждого разговора.

SELECT c.id, message, max(ts_send) FROM messages m 
JOIN conversations c ON m.conversations_id = c.id 
WHERE c.id IN ('.implode(',', $conversations_ids).') 
GROUP by c.id 
HAVING max(ts_send) = ?'; 

Возможно, запрос является неправильным в целом, просто хотел поделиться своей попыткой.

С уважением, Кристиан

+1

Не могли бы вы поделиться с sql friddle со структурой таблицы и некоторыми данными? IN ошибается, это PHP-код. –

+0

Кроме того, я не думаю, что вам нужна 'JOIN' здесь ... –

ответ

1
SELECT c.id, m.message, m.ts_send 
FROM conversations c LEFT JOIN messages m 
    ON c.id = m.conversations_id 
WHERE m.ts_send = 
    (SELECT MAX(m2.ts_send) 
    FROM messages m2 
    WHERE m2.conversations_id = m.conversations_id) 

LEFT JOIN гарантирует, что у вас есть строка для каждого разговора, независимо от того, есть ли у нее сообщения или нет. Это может быть ненужным, если это невозможно в вашей модели. в этом случае:

SELECT m.conversations_id, m.message, m.ts_send 
FROM messages m 
WHERE m.ts_send = 
    (SELECT MAX(m2.ts_send) 
    FROM messages m2 
    WHERE m2.conversations_id = m.conversations_id) 
+1

Ненужный 'JOIN' ...' 'conversations_id' также включен в' messages'-table. –

+0

Отредактирован ответ, чтобы объяснить использование JOIN. Я согласен, что в этом случае это может оказаться ненужным. – trogdor

+0

Спасибо! В моем случае разговор не может существовать без сообщения, поэтому мне не нужно ЛЕВАЯ ВСТУПИТЬ. Я просто добавил инструкцию IN в конце в дополнение к WHERE m.ts_send – Chris

0

Я думаю, что это то, что вы говорите.

mysql> create table messages (ts_send int, message char(30), id int); 
Query OK, 0 rows affected (0.01 sec) 

mysql> create table conversations (id int); 
Query OK, 0 rows affected (0.01 sec) 

Теперь некоторые данные.

mysql> insert into conversations values (1), (2), (3), (4); 
    Query OK, 4 rows affected (0.00 sec) 
    Records: 4 Duplicates: 0 Warnings: 0 


    mysql> insert into messages values (4, 'abc', 1), 
(5, 'pqr', 1), 
(4, 'abc', 2), 
(5, 'abc', 3), 
(6, 'abc', 4); 
    Query OK, 5 rows affected (0.01 sec) 
    Records: 5 Duplicates: 0 Warnings: 0 

И затем запрос.

mysql> select messages.id, message, ts_send 
from messages 
where ROW(id, ts_send) in 
(select messages.id, max(ts_send) 
from messages, conversations 
where messages.id = conversations.id group by id); 
    +------+---------+---------+ 
    | id | message | ts_send | 
    +------+---------+---------+ 
    | 1 | pqr  |  5 | 
    | 2 | abc  |  4 | 
    | 3 | abc  |  5 | 
    | 4 | abc  |  6 | 
    +------+---------+---------+ 
    4 rows in set (0.00 sec) 


    mysql> 

Справа?

* ДАННОЕ ИЗОБРАЖЕНИЕ было отражено, как комментарий от Marty McVry.

+0

Это не выводит само сообщение ... –

1

Если бы вы так же, как самый большой ts_send от каждого уникального conversations_id, вы можете использовать этот код:

SELECT * 
    FROM messages 
WHERE CONCAT(conversations_id, '_', ts_send) IN ( SELECT CONCAT(conversations_id, '_', MAX(ts_send)) 
                 FROM messages 
                GROUP BY conversations_id); 

SQL-fiddle

Что этот код делает это, он создает пары в conversations_id, вместе с самый большой ts_send. Затем он сравнивает это со всеми парами во всей таблице.

+0

Действительно хороший запрос, если бы я мог принять два ответа, я бы принял и ваш, но так как подход от трогдора был уже тем, что мне нужно, я принял его ответ. – Chris

1
SELECT c.id, m.message, m.ts_send 
FROM 
messages m, conversations c, 
(SELECT conversations_id, MAX(ts_send) as ts_send 
from messages 
group by conversations_id) s 
where s.conversations_id=m.conversations_id and s.ts_send=m.ts_send and 
c.id=m.conversations_id 
+0

Я поддерживаю, по сути, это то же самое решение, что и мое, но оно способствует использованию синтаксиса присоединения ANSI 89, который устарел более 20 лет. [Эта статья] (http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/08/bad-habits-to-kick-using-old-style-joins.aspx) может стоить прочитать. – GarethD

+0

Спасибо за голосование, теперь я буду избегать синтаксиса ANSI 89;) – Hedinn

3

MySql оптимизирует JOIN и намного лучше, чем коррелированные подзапросы, так что я буду ходить через присоединиться к подходу.

Первый шаг, чтобы получить максимальную ts_send за разговор:

SELECT conversations_id, MAX(ts_send) AS ts_send 
FROM messages 
GROUP BY conversations_id; 

Затем нужно JOIN это обратно к таблице сообщений, чтобы получить фактическое сообщение.Присоединиться на conversation_id и MAX (ts_send) гарантирует, что только последнее сообщение возвращается для каждого разговора:

SELECT messages.conversations_id, 
     messages.message, 
     Messages.ts_send 
FROM messages 
     INNER JOIN 
     ( SELECT conversations_id, MAX(ts_send) AS ts_send 
      FROM messages 
      GROUP BY conversations_id 
     ) MaxMessage 
      ON MaxMessage.conversations_id = messages.conversations_id 
      AND MaxMessage.ts_send = messages.ts_send; 

выше, должны получить, что вы после этого, если вы также не нужны разговоры вернулся, где не было никаких сообщений , В этом случае вам нужно будет выбрать из conversations и LEFT JOIN в приведенном выше запросе:

SELECT conversations.id, 
     COALESCE(messages.message, 'No Messages') AS Message, 
     messages.ts_send 
FROM conversations 
     LEFT JOIN 
     ( SELECT messages.conversations_id, 
        messages.message, 
        Messages.ts_send 
      FROM messages 
        INNER JOIN 
        ( SELECT conversations_id, MAX(ts_send) AS ts_send 
         FROM messages 
         GROUP BY conversations_id 
        ) MaxMessage 
         ON MaxMessage.conversations_id = messages.conversations_id 
         AND MaxMessage.ts_send = messages.ts_send 
     ) messages 
      ON messages.conversations_id = conversations.id; 

EDIT

Последний вариант выбора всех разговоров, независимо от того, имеют ли они сообщение будет лучше achived следующим образом:

SELECT conversations.id, 
     COALESCE(messages.message, 'No Messages') AS Message, 
     messages.ts_send 
FROM conversations 
     LEFT JOIN messages 
      ON messages.conversations_id = conversations.id 
     LEFT JOIN 
     ( SELECT conversations_id, MAX(ts_send) AS ts_send 
      FROM messages 
      GROUP BY conversations_id 
     ) MaxMessage 
      ON MaxMessage.conversations_id = messages.conversations_id 
      AND MaxMessage.ts_send = messages.ts_send 
WHERE messages.ts_send IS NULL 
OR  MaxMessage.ts_send IS NOT NULL; 

Благодаря здесь идет в spencer7593, кто предложил вышеупомянутое решение.

+0

Спасибо за усилия и приятное объяснение! – Chris

+1

+1. операция 'JOIN' обычно превосходит« IN (подзапрос) », хотя мы не замечаем этого, пока не начнем работать с большими наборами. Кроме того, последний запрос (для получения разговоров, которые не имеют связанных сообщений), что фактически можно сделать без добавления дополнительного встроенного представления. «FROM messages INNER» может быть изменен на «FROM цепочек» LEFT JOIN ON() LEFT JOIN() MaxMessage ON() LEFT JOIN messages' (изменение порядка таблицы и встроенного представления), без необходимости дополнительной строки Посмотреть. – spencer7593

+0

@ spencer7593 Это может быть достигнуто с помощью двух левых объединений ([демонстрация здесь] (http://www.sqlfiddle.com/#!2/2ef3b2/2)), однако меньше не всегда больше, и введение требуемого где предложение может снизить производительность. У меня есть только SQLFiddle, доступный для проверки запроса в MySQL, но в SQL-Server (я не знаю, что напрямую сопоставим), два левых соединения, в отличие от подхода, который я опубликовал, на самом деле имеет 2 меньше числа сканирования (6 и 8) и 12 менее логических чтений (24 - 36) на [этот пример] (http://www.sqlfiddle.com/#!3/f7664/2). – GarethD