2013-08-14 2 views
0

현재 쿼리에 어려움을 겪고 있으며 도움이 필요합니다.집계 함수로 정의 된 열에서 값 선택

messages { 
    ts_send, 
    message, 
    conversations_id 
} 

conversations { 
    id 
} 

내가 각 대화의 최신 ts_send을 갖는 메시지를 선택합니다 :

나는 두 테이블을 가지고있다. 3 개의 대화가있는 경우 3 개의 메시지가 표시됩니다.

다음 쿼리를 작성하기 시작했지만 각 대화의 최대 (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을 테이블 구조 및 일부 데이터와 공유 할 수 있습니까? PHP 코드가 IN이 잘못되었습니다. –

+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을 사용하면 메시지가 있는지 여부에 관계없이 각 대화마다 행을 사용할 수 있습니다. 모델에서 이것이 가능하지 않으면 불필요한 것일 수 있습니다. 이 경우 : MySQL의 최적화

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' 테이블에 포함되어 있습니다. –

+0

JOIN의 사용법을 설명하는 대답을 편집했습니다. 이 경우 불필요하다고 동의합니다. – trogdor

+0

감사! 제 경우에는 메시지없이 대화를 할 수 없으므로 LEFT JOIN을하지 않아도됩니다. 방금 전에 IN 문을 어디에 추가 했습니까? – 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

당신은 각각의 고유 한 conversations_id에서 ts_send 최대처럼, 당신은이 코드를 사용한다면 :

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

정말 좋은 질문입니다. 두 가지 대답을 받아 들일 수 있다면 나는 또한 당신의 의견을 받아 들일 것입니다. trogdor에서 접근은 이미 내가 필요로하는 것이 었습니다. 나는 그의 대답을 받아 들였습니다. – 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

는 상관 서브 쿼리보다 훨씬 더 조인, 그래서 가입 방법을 살펴 보겠습니다.

첫 번째 단계는 대화 당 최대 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는 위의 질의에 가입 :

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; 

편집

관계없이 메시지가 있는지 여부의 모든 대화를 선택하는 후자 것

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로 이동, 사람을 다음과 같이 더 나은 achived 수 위의 해결책을 제안했다.

+0

노력과 멋진 explonation에 감사드립니다! – Chris

+1

+1. 'JOIN' 연산은 대개'IN (subquery)'보다 성능이 뛰어납니다.하지만 우리가 큰 세트를 다룰 때까지는 실제로 알지 못합니다. 또한 마지막 쿼리 (관련 메시지가없는 대화를 얻으려면)가 추가 인라인 뷰없이 실제로 수행 할 수 있습니다. 'FROM messages INNER'는 여분의 인라인을 필요로하지 않고'LEOM JOIN ON() LEFT JOIN() LEFT JOIN()과 MaxMessage ON() LEFT JOIN messages' (테이블과 인라인보기의 순서 변경) 전망. – spencer7593

+0

@ spencer7593 두 개의 왼쪽 조인 ([demonstration here] (http://www.sqlfiddle.com/#!2/2ef3b2/2))을 사용하여 수행 할 수 있지만, 항상 적은 것은 아니지만 필수 항목 where 절은 성능을 저하시킬 수 있습니다. SQLFiddle은 MySQL에서 쿼리를 테스트 할 수 있지만 SQL-Server에서는 직접 비교할 수 없다는 것을 알고 있습니다. 두 개의 왼쪽 조인은 실제로 게시 한 접근 방식에 비해 2 스캔 횟수가 적습니다 (6 및 8). [이 예제] (http://www.sqlfiddle.com/#!3/f7664/2)에서 논리적 읽기 12 회 (24 - 36)가 적습니다. – GarethD