MySQL은 Group By를 사용할 때 잘못된 행을 보여줍니다
-
13-09-2019 - |
문제
두 개의 테이블이 있습니다.
article('id', 'ticket_id', 'incoming_time', 'to', 'from', 'message')
ticket('id', 'queue_id')
여기서 티켓은 지원 직원과 고객 사이의 이메일 스레드를 나타내고 기사는 스레드를 작성하는 개별 메시지입니다.
각 ticket_id에 대해 들어오는 시간 (UNIX 타임 스탬프로 표현 됨)이 가장 높은 기사를 찾고 있습니다. 이것은 현재 사용중인 쿼리입니다.
SELECT article.* , MAX(article.incoming_time) as maxtime
FROM ticket, article
WHERE ticket.id = article.ticket_id
AND ticket.queue_id = 1
GROUP BY article.ticket_id
예를 들어,
:article:
id --- ticket_id --- incoming_time --- to ------- from ------- message --------
11 1 1234567 help@ client@ I need help...
12 1 1235433 client@ help@ How can we help?
13 1 1240321 help@ client@ Want food!
...
:ticket:
id --- queue_id
1 1
...
그러나 결과는 내가 찾고있는 것 대신 가장 작은 기사 ID가있는 행으로 보입니다.
모든 조언은 대단히 감사하겠습니다!
해결책
이것은 대부분의 MySQL 프로그래머가 충돌하는 고전적인 장애물입니다.
- 열이 있습니다
ticket_id
그것이 바로 논쟁입니다GROUP BY
. 이 열의 뚜렷한 값은 그룹을 정의합니다. - 열이 있습니다
incoming_time
그것이 바로 논쟁입니다MAX()
. 각 그룹의 행 에서이 열에서 가장 큰 값은 다음의 값으로 리턴됩니다.MAX()
. - 다른 모든 열의 테이블 기사가 있습니다. 이 열에 대해 반환 된 값은
MAX()
값이 발생합니다.
데이터베이스는 최대 값이 발생하는 동일한 행에서 값을 원한다고 추론 할 수 없습니다.
다음 사례에 대해 생각해보십시오.
동일한 최대 값이 발생하는 여러 행이 있습니다. 열을 보여주기 위해 어떤 행을 사용해야하는지
article.*
?당신은 두 가지를 모두 반환하는 쿼리를 작성합니다
MIN()
그리고MAX()
. 이것은 합법적이지만 어떤 행이 있어야하는지article.*
보여 주다?SELECT article.* , MIN(article.incoming_time), MAX(article.incoming_time) FROM ticket, article WHERE ticket.id = article.ticket_id AND ticket.queue_id = 1 GROUP BY article.ticket_id
다음과 같은 집계 함수를 사용합니다
AVG()
또는SUM()
, 행에 그 값이없는 경우. 데이터베이스가 어떤 행을 표시할지 추측하는 방법은 무엇입니까?SELECT article.* , AVG(article.incoming_time) FROM ticket, article WHERE ticket.id = article.ticket_id AND ticket.queue_id = 1 GROUP BY article.ticket_id
대부분의 데이터베이스 브랜드와 SQL 표준 자체에서 허용되지 않습니다 모호성 때문에 이와 같은 쿼리를 작성합니다. 집계 함수 내부에 있지 않거나 GROUP BY
절.
MySQL이 더 허용됩니다. 그것은 당신이 이것을 할 수있게하고, 모호함없이 쿼리를 작성하기 위해 당신에게 맡길 수 있습니다. 모호성이있는 경우 그룹에서 물리적으로 먼저있는 행에서 값을 선택합니다 (그러나 이것은 저장 엔진에 달려 있음).
가치가있는 것에 대해 Sqlite는 또한이 행동을 가지고 있지만 마지막 모호성을 해결하기 위해 그룹에서 행을 행하십시오. 그림을 이동. SQL 표준이해야 할 일을 말하지 않으면 공급 업체 구현에 달려 있습니다.
다음은 귀하의 문제를 해결할 수있는 쿼리입니다.
SELECT a1.* , a1.incoming_time AS maxtime
FROM ticket t JOIN article a1 ON (t.id = a1.ticket_id)
LEFT OUTER JOIN article a2 ON (t.id = a2.ticket_id
AND a1.incoming_time < a2.incoming_time)
WHERE t.queue_id = 1
AND a2.ticket_id IS NULL;
다시 말해, 행을 찾으십시오 (a1
) 다른 행이없는 (a2
) 동일하게 ticket_id
그리고 더 큰 incoming_time
. 더 큰 경우 incoming_time
발견됩니다. 왼쪽 외부 결합은 일치 대신 NULL을 반환합니다.
다른 팁
SELECT a1.* FROM article a1
JOIN
(SELECT MAX(a2.incoming_time) AS maxtime
FROM article a2
JOIN ticket ON (a2.ticketid=ticket.id)
WHERE ticket.queue_id=1) xx
ON (a1.incoming_time=xx.maxtime);