업데이트 2 위의 sqlfiddle 을 추가한 후 질문이 찬성된 비율이 답변의 찬성 비율을 초과했음을 알았습니다. 그런 의도는 아니었습니다! 바이올린은 답변, 특히 허용되는 답변을 기반으로 합니다.
첫눈에...
MAX 집계 함수가 있는 GROUP BY 절만 있으면 됩니다.
SELECT id, MAX(rev) FROM YourTable GROUP BY id
결코 간단하지 않죠?
content 열도 필요하다는 것을 알게 되었습니다.
이것은 SQL에서 매우 일반적인 질문입니다. 일부 그룹 식별자별로 열에서 최대 값이 있는 행에 대한 전체 데이터를 찾습니다. 직장생활을 하면서 그런 말을 많이 들었다. 사실 제가 현 직장의 기술 면접에서 답변한 질문 중 하나였습니다.
실제로 StackOverflow 커뮤니티에서 다음과 같은 질문을 처리하기 위해 단일 태그를 생성하는 것은 매우 일반적입니다. Greatest-n-per-group .
기본적으로 해당 문제를 해결하기 위한 두 가지 접근 방식이 있습니다.
단순 group-identifier, max-value-in-group 하위 쿼리로 조인
이 접근 방식에서는 먼저 group-identifier, max-value-in-group (위에서 이미 해결됨)을 찾습니다. group-identifier 및 max-value-in-group 모두에서 동일한 하위 쿼리에 테이블을 조인합니다.
SELECT a.id, a.rev, a.contents FROM YourTable a INNER JOIN ( SELECT id, MAX(rev) rev FROM YourTable GROUP BY id ) b ON a.id = b.id AND a.rev = b.rev
왼쪽 자신과 조인, 조인 조건 및 필터 조정
이 접근 방식에서는 테이블 자체와 함께 왼쪽 조인을 수행했습니다. 평등은 group-identifier 있습니다. 그런 다음 2개의 스마트 동작:
두 번째 조인 조건은 왼쪽 값이 오른쪽 값보다 작습니다.
1단계를 수행하면 실제로 최대값이 있는 행의 오른쪽에 NULLLEFT JOIN , 기억하십니까?). 그런 다음 조인된 결과를 필터링하여 오른쪽이 NULL 행만 표시합니다.
그래서 당신은 다음과 같이 끝납니다.
SELECT a.* FROM YourTable a LEFT OUTER JOIN YourTable b ON a.id = b.id AND a.rev < b.rev WHERE b.id IS NULL;
결론
두 접근 방식 모두 똑같은 결과를 가져옵니다.
group-identifier 대해 max-value-in-group 이 있는 두 개의 행이 있는 경우 두 행 모두 두 접근 방식의 결과에 모두 포함됩니다.
두 접근 방식 모두 SQL ANSI와 호환되므로 "맛"에 관계없이 선호하는 RDBMS와 함께 작동합니다.
두 접근 방식 모두 성능 친화적이지만 마일리지가 다를 수 있습니다(RDBMS, DB 구조, 인덱스 등). 그래서 당신은 다른 벤치 마크를 통해 한 가지 방법을 선택합니다. 그리고 자신에게 가장 의미가 있는 것을 선택하십시오.
Adriano Carneiro
내 취향은 가능한 한 적은 코드를 사용하는 것입니다 ...
IN 을 사용하여 수행할 수 있습니다.
SELECT * FROM t1 WHERE (id,rev) IN ( SELECT id, MAX(rev) FROM t1 GROUP BY id )
내 생각에는 덜 복잡하고 ... 더 읽기 쉽고 유지 관리하기 쉽습니다.
Kevin Burton
나는 대답이 SQL 창 기능 솔루션을 제공하지 않았다는 사실에 놀랐습니다.
SELECT a.id, a.rev, a.contents FROM (SELECT id, rev, contents, ROW_NUMBER() OVER (PARTITION BY id ORDER BY rev DESC) rank FROM YourTable) a WHERE a.rank = 1
SQL 표준 ANSI/ISO 표준 SQL:2003에 추가되었고 이후 ANSI/ISO 표준 SQL:2008로 확장되어 현재 모든 주요 공급업체에서 창(또는 창) 기능을 사용할 수 있습니다. RANK, DENSE_RANK, PERSENT_RANK 와 같이 동점 문제를 처리하는 데 사용할 수 있는 더 많은 유형의 순위 함수가 있습니다.
topchef
또 다른 솔루션은 상관 하위 쿼리를 사용하는 것입니다.
select yt.id, yt.rev, yt.contents from YourTable yt where rev = (select max(rev) from YourTable st where yt.id=st.id)
(id,rev)에 인덱스가 있으면 하위 쿼리가 거의 간단한 조회로 렌더링됩니다.
다음은 그룹 크기가 1-3인 ~1백만 레코드의 InnoDB 테이블을 사용한 MySQL 측정을 기반으로 한 @AdrianCarneiro의 답변(하위 쿼리, 왼쪽 조인)의 솔루션과 비교한 것입니다.
전체 테이블 스캔의 경우 하위 쿼리/왼쪽 조인/상관 타이밍은 서로 6/8/9로 관련되지만 직접 조회 또는 일괄 처리( id in (1,2,3) )와 관련하여 하위 쿼리는 다른 것보다 훨씬 느립니다( 하위 쿼리를 다시 실행하기 때문에). 그러나 나는 속도면에서 leftjoin과 상관 솔루션을 구별할 수 없었습니다.
마지막 참고 사항으로, leftjoin은 그룹에서 n*(n+1)/2개의 조인을 생성하므로 성능은 그룹 크기에 따라 크게 영향을 받을 수 있습니다...
Vajk Hermecz
성능을 보장할 수는 없지만 Microsoft Excel의 한계에서 영감을 얻은 트릭이 있습니다. 몇 가지 좋은 기능이 있습니다
좋은 물건
동점이 있더라도 하나의 "최대 레코드"만 강제로 반환해야 합니다(때로는 유용함).
조인이 필요하지 않습니다.
접근하다
약간 보기 흉하고 rev 열의 유효한 값 범위에 대해 알고 있어야 합니다. rev 열이 소수점을 포함하여 0.00에서 999 사이의 숫자라는 것을 알고 있지만 소수점 오른쪽에 두 자리만 있을 것이라고 가정해 보겠습니다(예: 34.17이 유효한 값임).
문제의 요지는 원하는 데이터와 함께 기본 비교 필드를 문자열로 연결/포장하여 단일 합성 열을 생성한다는 것입니다. 이런 식으로 SQL의 MAX() 집계 함수가 모든 데이터를 반환하도록 할 수 있습니다(단일 열로 압축되었기 때문에). 그런 다음 데이터의 압축을 풀어야 합니다.
SQL로 작성된 위의 예를 보면 다음과 같습니다.
SELECT id, CAST(SUBSTRING(max(packed_col) FROM 2 FOR 6) AS float) as max_rev, SUBSTRING(max(packed_col) FROM 11) AS content_for_max_rev FROM (SELECT id, CAST(1000 + rev + .001 as CHAR) || '---' || CAST(content AS char) AS packed_col FROM yourtable ) GROUP BY id
패킹은 예를 들면되도록 REV의 값에 관계없이 공지 된 글자의 숫자로 올린다 열을 강제로 시작
3.2는 1003.201이 됩니다.
57은 1057.001이 됩니다.
923.88은 1923.881이 됩니다.
올바르게 수행하면 두 숫자의 문자열 비교는 두 숫자의 숫자 비교와 동일한 "최대값"을 산출해야 하며 하위 문자열 함수를 사용하여 원래 숫자로 쉽게 다시 변환할 수 있습니다. 어디에나).
David Foster
고유 식별자? 예! 고유 식별자!
MySQL DB를 개발하는 가장 좋은 방법 중 하나 는 각 idAUTOINCREMENT (출처 MySQL.com)로 지정하는 것입니다. 이것은 여기에서 다루기에는 너무 많은 다양한 이점을 허용합니다. 질문의 문제는 해당 예제에 중복 ID가 있다는 것입니다. 이것은 고유 식별자의 이러한 엄청난 이점을 무시하는 동시에 이미 이것에 익숙한 사람들에게 혼란을 줍니다.
최신 버전의 MySQL은 ONLY_FULL_GROUP_BY 활성화된 상태로 제공되며 여기에 있는 많은 솔루션은 이 조건에서 테스트에 실패합니다.
그럼에도 불구하고, 우리는 간단하게 선택할 수 있습니다 DISTINCTsomeuniquefield,MAX(whateverotherfieldtoselect) , ( * somethirdfield ) 결과 또는 방법 쿼리 작품을 이해하는 걱정을 등을하고 있습니다 :
SELECT DISTINCT t1.id, MAX(t1.rev), MAX(t2.content) FROM Table1 AS t1 JOIN Table1 AS t2 ON t2.id = t1.id AND t2.rev = ( SELECT MAX(rev) FROM Table1 t3 WHERE t3.id = t1.id ) GROUP BY t1.id;
SELECT DISTINCT Table1.id, max(Table1.rev), max(Table2.content) : 반환 DISTINCT somefield, MAX() some otherfield, 마지막 MAX() 는 중복됩니다. 왜냐하면 그것이 하나의 행이라는 것을 알고 있기 때문입니다. 쿼리.
FROM Employee : 검색된 테이블입니다.
JOIN Table1 AS Table2 ON Table2.rev = Table1.rev : 두 번째 테이블을 첫 번째 테이블에 조인합니다. max(table1.rev)의 주석을 가져와야 하기 때문입니다.
GROUP BY Table1.id : 상위 정렬된 각 직원의 급여 행을 강제로 반환된 결과로 만듭니다.
OP의 질문에서 "content"가 "..."였기 때문에 이것이 작동하는지 테스트할 방법이 없습니다. 그래서 "..a", "..b"로 변경했습니다. 이제 결과가 올바른지 확인할 수 있습니다.
id max(Table1.rev) max(Table2.content) 1 3 ..d 2 1 ..b
왜 깨끗합니까?DISTINCT() , MAX() 등은 모두 MySQL 인덱스를 훌륭하게 사용합니다. 이것은 더 빠를 것입니다. 또는 인덱싱이 있고 모든 행을 보는 쿼리와 비교하는 경우 훨씬 빠릅니다.
오리지널 솔루션
ONLY_FULL_GROUP_BY 비활성화하면 GROUP BY 계속 사용할 수 있지만 id가 아닌 급여에만 사용합니다.
SELECT * FROM (SELECT * FROM Employee ORDER BY Salary DESC) AS employeesub GROUP BY employeesub.Salary;
SELECT * : 모든 필드를 반환합니다.
FROM Employee : 검색된 테이블입니다.
(SELECT *...) 하위 쿼리 : 급여별로 정렬된 모든 사람을 반환합니다.
GROUP BY employeesub.Salary : 각 직원의 최상위 정렬된 급여 행을 강제로 반환된 결과로 만듭니다.
고유 행 솔루션
관계형 데이터베이스 의 정의: "테이블의 각 행에는 고유한 키가 있습니다." 즉, 질문의 예에서 id는 고유해야 하며 이 경우 다음과 같이 할 수 있습니다.
SELECT * FROM Employee WHERE Employee.id = 12345 ORDER BY Employee.Salary DESC LIMIT 1
바라건대 이것이 문제를 해결하고 모든 사람들이 DB에서 무슨 일이 일어나고 있는지 더 잘 이해하는 데 도움이 되는 솔루션입니다.
HoldOffHunger
이 같은?
SELECT yourtable.id, rev, content FROM yourtable INNER JOIN ( SELECT id, max(rev) as maxrev FROM yourtable GROUP BY id ) AS child ON (yourtable.id = child.id) AND (yourtable.rev = maxrev)
Marc B
작업을 수행하는 또 다른 방법은 OVER PARTITION 절에서 MAX()
SELECT t.* FROM ( SELECT id ,rev ,contents ,MAX(rev) OVER (PARTITION BY id) as max_rev FROM YourTable ) t WHERE t.rev = t.max_rev
이 게시물에 이미 문서화된 다른 ROW_NUMBER() OVER PARTITION 솔루션은 다음과 같습니다.
SELECT t.* FROM ( SELECT id ,rev ,contents ,ROW_NUMBER() OVER (PARTITION BY id ORDER BY rev DESC) rank FROM YourTable ) t WHERE t.rank = 1
이 2 SELECT는 Oracle 10g에서 잘 작동합니다.
ROW_NUMBER() 솔루션보다 확실히 더 빠르게 실행됩니다. MAX() 복잡도는 O(n) 이고 ROW_NUMBER() 복잡도는 최소 O(n.log(n)) 이기 때문입니다. 여기서 n 은 테이블의 레코드 수를 나타냅니다!
schlebe
이 문제에 NOT EXIST 기반 솔루션을 사용하고 싶습니다.
SELECT id, rev -- you can select other columns here FROM YourTable t WHERE NOT EXISTS ( SELECT * FROM YourTable t WHERE t.id = id AND rev > t.rev )
이렇게 하면 그룹 내에서 최대 값을 가진 모든 레코드가 선택되고 다른 열을 선택할 수 있습니다.
Bulat
SELECT * FROM Employee where Employee.Salary in (select max(salary) from Employee group by Employe_id) ORDER BY Employee.Salary
guru008
내가 거의 언급하지 않은 세 번째 솔루션은 MySQL에만 해당되며 다음과 같습니다.
SELECT id, MAX(rev) AS rev , 0+SUBSTRING_INDEX(GROUP_CONCAT(numeric_content ORDER BY rev DESC), ',', 1) AS numeric_content FROM t1 GROUP BY id
예, 끔찍해 보이지만(문자열 및 역으로 변환 등) 제 경험상 일반적으로 다른 솔루션보다 빠릅니다. 내 사용 사례에만 해당될 수 있지만 수백만 개의 레코드와 많은 고유 ID가 있는 테이블에서 사용했습니다. 아마도 MySQL이 다른 솔루션을 최적화하는 데 매우 좋지 않기 때문일 수 있습니다(최소한 이 솔루션을 생각해 낸 5.0일 동안).
한 가지 중요한 점은 GROUP_CONCAT이 구축할 수 있는 문자열의 최대 길이가 있다는 것입니다. group_concat_max_len 변수를 설정하여 이 제한을 높이고 싶을 것입니다. 행 수가 많은 경우 확장에 제한이 있음을 명심하십시오.
어쨌든 콘텐츠 필드가 이미 텍스트인 경우 위의 내용이 직접 작동하지 않습니다. 이 경우 \0과 같은 다른 구분 기호를 사용하고 싶을 것입니다. 또한 group_concat_max_len 제한에 더 빨리 도달하게 됩니다.
Jannes
내 생각에, 당신이 이것을 원하십니까?
select * from docs where (id, rev) IN (select id, max(rev) as rev from docs group by id order by id)
mySQL 은 아니지만 이 질문을 찾고 SQL을 사용하는 다른 사람들을 위해 가장 큰 그룹당 문제를 해결하는 또 다른 방법은 MS SQL에서 Cross Apply 를 사용하는 것입니다.
WITH DocIds AS (SELECT DISTINCT id FROM docs) SELECT d2.id, d2.rev, d2.content FROM DocIds d1 CROSS APPLY ( SELECT Top 1 * FROM docs d WHERE d.id = d1.id ORDER BY rev DESC ) d2
이것은 이 문제와 관련하여 가장 인기 있는 질문이므로 여기에 다른 답변도 다시 게시하겠습니다.
이 작업을 수행하는 더 간단한 방법이 있는 것 같습니다(그러나 MySQL에서만 ).
select * from (select * from mytable order by id, rev desc ) x group by id
이 문제에 대한 간결하고 우아한 답변을 제공한 이 질문에 대한 사용자 Bohemian의 답변을 인정해 주십시오.
편집: 이 솔루션은 많은 사람들에게 효과가 있지만 장기적으로 안정적이지 않을 수 있습니다. MySQL은 GROUP BY 문이 GROUP BY 목록에 없는 열에 대해 의미 있는 값을 반환할 것이라고 보장하지 않기 때문입니다. 따라서 이 솔루션을 사용하는 데 따른 위험은 자신이 감수해야 합니다!
Yuriy Nakonechnyy
나는 이것을 사용할 것입니다 :
select t.* from test as t join (select max(rev) as rev from test group by id) as o on o.rev = t.rev
하위 쿼리 SELECT는 아마도 너무 효율적이지 않지만 JOIN 절에서 사용할 수 있는 것 같습니다. 저는 쿼리 최적화 전문가는 아니지만 MySQL, PostgreSQL, FireBird에서 시도했으며 매우 잘 작동합니다.
이 스키마는 다중 조인 및 WHERE 절과 함께 사용할 수 있습니다. 그것은 내 작업 예입니다 (테이블 "firmy"에 대한 귀하의 문제와 동일하게 해결).
select * from platnosci as p join firmy as f on p.id_rel_firmy = f.id_rel join (select max(id_obj) as id_obj from firmy group by id_rel) as o on o.id_obj = f.id_obj and p.od > '2014-03-01'
10대 이상의 레코드가 있는 테이블에서 요청되며 실제로 너무 강하지 않은 머신에서는 0.01초 미만이 소요됩니다.
나는 IN 절을 사용하지 않을 것입니다(위의 어딘가에서 언급했듯이). IN은 하위 쿼리에 구축된 쿼리 필터가 아닌 짧은 상수 목록과 함께 사용하기 위해 제공됩니다. IN의 하위 쿼리는 스캔된 모든 레코드에 대해 수행되어 쿼리 시간이 매우 오래 걸릴 수 있기 때문입니다.
Marek Wysmułek
select 문에 많은 필드가 있고 최적화된 코드를 통해 모든 필드에 대한 최신 값을 원하는 경우:
select * from (select * from table_name order by id,rev desc) temp group by id
seahawk
이것은 어떤가요:
SELECT all_fields.* FROM (SELECT id, MAX(rev) FROM yourtable GROUP BY id) AS max_recs LEFT OUTER JOIN yourtable AS all_fields ON max_recs.id = all_fields.id
inor
이 솔루션은 YourTable에서 하나만 선택하므로 더 빠릅니다. sqlfiddle.com의 테스트에 따르면 MySQL 및 SQLite에서만 작동합니다(SQLite의 경우 DESC 제거). 내가 익숙하지 않은 다른 언어에서 작동하도록 조정할 수 있습니다.
SELECT * FROM ( SELECT * FROM ( SELECT 1 as id, 1 as rev, 'content1' as content UNION SELECT 2, 1, 'content2' UNION SELECT 1, 2, 'content3' UNION SELECT 1, 3, 'content4' ) as YourTable ORDER BY id, rev DESC ) as YourTable GROUP BY id
plavozont
여기에 좋은 방법이 있습니다
다음 코드를 사용하십시오.
with temp as ( select count(field1) as summ , field1 from table_name group by field1 ) select * from temp where summ = (select max(summ) from temp)
shay
나는 어떤 열로 레코드의 순위를 매겨서 이것을 하는 것을 좋아합니다. id 그룹화된 rev 값의 순위를 지정합니다. rev 를 가진 사람들은 더 낮은 순위를 가질 것입니다. 따라서 가장 높은 rev 는 1의 순위를 갖습니다.
select id, rev, content from (select @rowNum := if(@prevValue = id, @rowNum+1, 1) as row_num, id, rev, content, @prevValue := id from (select id, rev, content from YOURTABLE order by id asc, rev desc) TEMP, (select @rowNum := 1 from DUAL) X, (select @prevValue := -1 from DUAL) Y) TEMP where row_num = 1;
변수를 도입하면 전체 작업이 느려지는지 확실하지 않습니다. 그러나 적어도 나는 YOURTABLE 두 번 쿼리하지 않습니다.
user5124980
rev 필드를 역순으로 정렬한 다음 rev 값이 가장 높은 각 그룹의 첫 번째 행을 제공하는 id별로 그룹화했습니다.
SELECT * FROM (SELECT * FROM table1 ORDER BY id, rev DESC) X GROUP BY X.id;
Select a.id , a.rev, a.content from Table1 a inner join (SELECT id, max(rev) rev FROM Table1 GROUP BY id) x on x.id =a.id and x.rev =a.rev
Abdul Samad
이 답변 중 어느 것도 나를 위해 일하지 않았습니다.
이것이 나를 위해 일한 것입니다.
with score as (select max(score_up) from history) select history.* from score, history where history.score_up = score.max
qaisjp
다음은 해당 필드에 대한 최대값이 있는 필드로만 레코드를 검색하는 또 다른 솔루션입니다. 이것은 내가 작업하는 플랫폼인 SQL400에서 작동합니다. 이 예에서 필드 FIELD5의 최대값이 있는 레코드는 다음 SQL 문에 의해 검색됩니다.
SELECT A.KEYFIELD1, A.KEYFIELD2, A.FIELD3, A.FIELD4, A.FIELD5 FROM MYFILE A WHERE RRN(A) IN (SELECT RRN(B) FROM MYFILE B WHERE B.KEYFIELD1 = A.KEYFIELD1 AND B.KEYFIELD2 = A.KEYFIELD2 ORDER BY B.FIELD5 DESC FETCH FIRST ROW ONLY)
Cesar
설명
이것은 순수한 SQL이 아닙니다. 이것은 SQLAlchemy ORM을 사용합니다.
SQLAlchemy 도움말을 찾아 여기에 왔으므로 Adrian Carneiro의 답변을 python/SQLAlchemy 버전, 특히 외부 조인 부분과 복제하겠습니다.
이 쿼리는 다음 질문에 답합니다.
"이 레코드 그룹(동일한 ID 기반)에서 가장 높은 버전 번호를 가진 레코드를 반환할 수 있습니까?"
이를 통해 레코드를 복제하고 업데이트하고 버전 번호를 늘리고 시간이 지남에 따라 변경 사항을 표시할 수 있는 방식으로 이전 버전의 복사본을 가질 수 있습니다.
나는 내 자신의 문제를 해결하기 위해 아래를 사용했습니다. 먼저 임시 테이블을 만들고 고유 ID당 최대 회전 값을 삽입했습니다.
CREATE TABLE #temp1 ( id varchar(20) , rev int ) INSERT INTO #temp1 SELECT a.id, MAX(a.rev) as rev FROM ( SELECT id, content, SUM(rev) as rev FROM YourTable GROUP BY id, content ) as a GROUP BY a.id ORDER BY a.id
그런 다음 이 최대값(#temp1)을 가능한 모든 ID/콘텐츠 조합에 결합했습니다. 이렇게 하면 최대가 아닌 ID/콘텐츠 조합을 자연스럽게 걸러내고 각각에 대한 유일한 최대 회전 값만 남게 됩니다.
SELECT a.id, a.rev, content FROM #temp1 as a LEFT JOIN ( SELECT id, content, SUM(rev) as rev FROM YourTable GROUP BY id, content ) as b on a.id = b.id and a.rev = b.rev GROUP BY a.id, a.rev, b.content ORDER BY a.id
Richard Ball
rev 와 id 를 MAX() 대한 하나의 maxRevId 값으로 결합한 다음 원래 값으로 다시 분할할 때 조인 없이 선택할 수 있습니다.
SELECT maxRevId & ((1 << 32) - 1) as id, maxRevId >> 32 AS rev FROM (SELECT MAX(((rev << 32) | id)) AS maxRevId FROM YourTable GROUP BY id) x;
단일 테이블 대신 복합 조인이 있는 경우 특히 빠릅니다. 전통적인 접근 방식을 사용하면 복잡한 조인이 두 번 수행됩니다.
rev 와 id 가 INT UNSIGNED (32비트)이고 결합된 값이 BIGINT UNSIGNED (64비트)에 맞는 경우 비트 함수로 간단합니다. id & rev 가 32비트 값보다 크거나 여러 열로 구성된 경우 값을 예를 들어 MAX() 적합한 패딩이 있는 이진 값으로 결합해야 합니다.