MySQL "Group By"및 "Order By"


96

전자 메일 테이블에서 여러 행을 선택하고 보낸 사람별로 그룹화 할 수 있기를 원합니다. 내 쿼리는 다음과 같습니다.

SELECT 
    `timestamp`, `fromEmail`, `subject`
FROM `incomingEmails` 
GROUP BY LOWER(`fromEmail`) 
ORDER BY `timestamp` DESC

쿼리는 내가 원하는대로 거의 작동합니다. 전자 메일로 그룹화 된 레코드를 선택합니다. 문제는 제목과 타임 스탬프가 특정 전자 메일 주소에 대한 가장 최근 레코드와 일치하지 않는다는 것입니다.

예를 들어 다음을 반환 할 수 있습니다.

fromEmail: john@example.com, subject: hello
fromEmail: mark@example.com, subject: welcome

데이터베이스의 레코드가 다음과 같은 경우 :

fromEmail: john@example.com, subject: hello
fromEmail: john@example.com, subject: programming question
fromEmail: mark@example.com, subject: welcome

"프로그래밍 질문"제목이 가장 최근 인 경우 이메일을 그룹화 할 때 MySQL이 해당 레코드를 선택하도록하려면 어떻게해야합니까?

답변:


140

간단한 해결책은 먼저 ORDER 문을 사용하여 쿼리를 하위 선택으로 래핑하고 나중에 GROUP BY를 적용하는 것입니다 .

SELECT * FROM ( 
    SELECT `timestamp`, `fromEmail`, `subject`
    FROM `incomingEmails` 
    ORDER BY `timestamp` DESC
) AS tmp_table GROUP BY LOWER(`fromEmail`)

이것은 조인을 사용하는 것과 비슷하지만 훨씬 더 멋지게 보입니다.

GROUP BY 절과 함께 SELECT에서 집계되지 않은 열을 사용하는 것은 비표준입니다. MySQL은 일반적으로 찾은 첫 번째 행의 값을 반환하고 나머지는 버립니다. 모든 ORDER BY 절은 반환 된 열 값에만 적용되며 폐기 된 열에는 적용되지 않습니다.

중요 업데이트 실제로 작동하는 데 사용되는 집계되지 않은 열을 선택하지만 의존해서는 안됩니다. MySQL 문서에 따르면 "이것은 GROUP BY에 이름이 지정되지 않은 각 집계되지 않은 열의 모든 값이 각 그룹에 대해 동일 할 때 주로 유용합니다. 서버는 각 그룹의 자유롭게 선택할 수 있으므로 동일하지 않은 경우 값이 선택은 불확실 합니다. "

현재 5.7.5 ONLY_FULL_GROUP_BY는 쿼리 오류 원인 기본 그래서 비 집계 열 (ER_WRONG_FIELD_WITH_GROUP)으로 사용 가능

@mikep이 지적했듯이 해결책은 5.7 이상에서 ANY_VALUE () 를 사용하는 것입니다.

참조 http://www.cafewebmaster.com/mysql-order-sort-group https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html //dev.mysql : HTTPS를 .com / doc / refman / 5.7 / en / group-by-handling.html https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_any-value


7
나는 몇 년 전에 같은 해결책을 내 놓았고 그 훌륭한 해결책이었습니다. b7kich에 대한 찬사. 하지만 여기에 두 가지 문제가 있습니다 ... GROUP BY는 대소 문자를 구분하지 않으므로 LOWER ()가 필요하지 않습니다. 둘째, $ userID는 PHP에서 직접 변수로 표시됩니다. $ userID가 사용자가 제공하고 강제하지 않으면 코드가 SQL 주입에 취약 할 수 있습니다. 정수가됩니다.
velcrow

중요 업데이트는 MariaDB에도 적용됩니다. mariadb.com/kb/en/mariadb/…
Arthur Shipkowski 2017 년

1
As of 5.7.5 ONLY_FULL_GROUP_BY is enabled by default, i.e. it's impossible to use non-aggregate columns.SQL 모드는 관리자 권한없이 런타임 중에 변경할 수 있으므로 ONLY_FULL_GROUP_BY를 비활성화하는 것이 매우 쉽습니다. 예 : SET SESSION sql_mode = '';. 데모 : db-fiddle.com/f/esww483qFQXbXzJmkHZ8VT/3
mikep

1
또는 ONLY_FULL_GROUP_BY를 우회하는 또 다른 대안은 ANY_VALUE ()를 사용하는 것입니다. 더보기 dev.mysql.com/doc/refman/8.0/en/...
mikep

42

다음은 한 가지 접근 방식입니다.

SELECT cur.textID, cur.fromEmail, cur.subject, 
     cur.timestamp, cur.read
FROM incomingEmails cur
LEFT JOIN incomingEmails next
    on cur.fromEmail = next.fromEmail
    and cur.timestamp < next.timestamp
WHERE next.timestamp is null
and cur.toUserID = '$userID' 
ORDER BY LOWER(cur.fromEmail)

기본적으로 테이블 자체를 조인하여 이후 행을 검색합니다. where 절에서 나중에 행이있을 수 없다고 명시합니다. 이것은 당신에게 최신 행만 제공합니다.

타임 스탬프가 동일한 이메일이 여러 개있을 수있는 경우이 쿼리를 수정해야합니다. 이메일 테이블에 증분 ID 열이있는 경우 JOIN을 다음과 같이 변경합니다.

LEFT JOIN incomingEmails next
    on cur.fromEmail = next.fromEmail
    and cur.id < next.id

textID모호 하다고 말했다 = /
John Kurlak

1
그런 다음 ambuigity를 제거하고 cur.textID와 같은 테이블 이름을 앞에 붙입니다. 대답도 변경되었습니다.
Andomar 2009-06-30

이것이 Doctrine DQL로 할 수있는 유일한 해결책입니다.
VisioN

여러 열에 대해 자체 조인을 시도 할 때는 작동하지 않습니다. IE는 최신 이메일과 최신 사용자 이름을 찾으려고 할 때 단일 쿼리에서이 작업을 수행하기 위해 여러 개의 self left join이 필요합니다.
Loveen Dyall

비 미래의 날짜로 결과 집합을 제한하기 위해 과거와 미래의 타임 스탬프 / 날짜, 작업 할 때, 당신은 또 다른 조건을 추가 할 필요가 LEFT JOIN기준AND next.timestamp <= UNIX_TIMESTAMP()
fyrye

32

이미 답장에서 지적했듯이 GROUP BY가 창에서 임의로 레코드를 선택하기 때문에 현재 답변이 잘못되었습니다.

MySQL 5.6 또는 MySQL 5.7과 함께 사용 ONLY_FULL_GROUP_BY하는 경우 올바른 (결정적) 쿼리는 다음과 같습니다.

SELECT incomingEmails.*
  FROM (
    SELECT fromEmail, MAX(timestamp) `timestamp`
    FROM incomingEmails
    GROUP BY fromEmail
  ) filtered_incomingEmails
  JOIN incomingEmails USING (fromEmail, timestamp)
GROUP BY fromEmail, timestamp

쿼리를 효율적으로 실행하려면 적절한 인덱싱이 필요합니다.

단순화를 위해 LOWER()대부분의 경우 사용되지 않는를 제거했습니다 .


2
정답이되어야합니다. 제 웹 사이트에서 이와 관련된 버그를 발견했습니다. 는 order by다른 답변의 부속 선택에 전혀 영향을주지 않습니다.
Jette

1
OMG,이 대답을 받아 들여주십시오. 받아 들여진 것은 내 시간의 5 시간을 낭비했다 :(
Richard Kersey

29

다음과 같이 GROUP BY로 쿼리를 래핑하여 ORDER BY 뒤에 GROUP BY를 수행하십시오.

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from

1
따라서 GROUP BY`는 자동으로 최신 time또는 최신 time또는 무작위를 선택합니까?
xrDDDD

1
우리가 주문하고 있기 때문에 가장 최신 시간을 선택하고 time DESC그룹 기준은 첫 번째 (최신)를 취합니다.
11101101b 2013 년

이제 mysql 5.1에서 VIEWS의 하위 선택에 JOINS를 할 수 있다면. 해당 기능이 최신 릴리스에서 제공 될 수 있습니다.
IcarusNM

21

SQL 표준에 따라 선택 목록에서 집계되지 않은 열을 사용할 수 없습니다. MySQL은 그러한 사용을 허용하지만 (uless ONLY_FULL_GROUP_BY 모드 사용) 결과를 예측할 수 없습니다.

ONLY_FULL_GROUP_BY

먼저 fromEmail, MIN (읽기)을 선택한 다음 두 번째 쿼리 (또는 하위 쿼리)-제목을 선택해야합니다.


MIN (read)는 "read"의 최소값을 반환합니다. 그는 아마도 최신 이메일의 "읽기"플래그를 대신 찾고있을 것입니다.
Andomar 2009-06-30

2

나는 표시된 것보다 더 복잡한 쿼리에 대해이 두 가지 접근 방식으로 어려움을 겪었습니다. 하위 쿼리 접근 방식은 내가 어떤 인덱스를 입력하더라도 끔찍하게 비효율적이었고 Hibernate를 통해 외부 자체 조인을 얻을 수 없었기 때문입니다.

이를 수행하는 가장 좋은 (그리고 가장 쉬운) 방법은 필요한 필드의 연결을 포함하도록 구성된 항목별로 그룹화 한 다음 SELECT 절에서 표현식을 사용하여 끌어내는 것입니다. MAX ()를 수행해야하는 경우 MAX ()하려는 필드가 항상 연결된 엔티티의 가장 중요한 끝에 있는지 확인하십시오.

이를 이해하기위한 핵심은 이러한 다른 필드가 Max ()를 충족하는 엔티티에 대해 변하지 않는 경우에만 쿼리가 의미가 있으므로 정렬 측면에서 다른 연결 부분을 무시할 수 있다는 것입니다. 이 링크의 맨 아래에서이를 수행하는 방법을 설명합니다. http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html

필드의 연결을 미리 계산하기 위해 (트리거와 같은) 삽입 / 업데이트 이벤트를 얻을 수 있다면이를 인덱싱 할 수 있으며 쿼리는 그룹이 실제로 MAX를 원했던 필드 위에있는 것처럼 빠릅니다. ). 이를 사용하여 최대 여러 필드를 가져올 수도 있습니다. 중첩 된 집합으로 표현 된 다차원 트리에 대한 쿼리를 수행하는 데 사용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.