각 그룹에서 마지막 레코드 검색-MySQL


954

messages아래와 같이 데이터가 포함 된 테이블 이 있습니다.

Id   Name   Other_Columns
-------------------------
1    A       A_data_1
2    A       A_data_2
3    A       A_data_3
4    B       B_data_1
5    B       B_data_2
6    C       C_data_1

query를 실행하면 다음 select * from messages group by name과 같은 결과가 나타납니다.

1    A       A_data_1
4    B       B_data_1
6    C       C_data_1

어떤 쿼리가 다음 결과를 반환합니까?

3    A       A_data_3
5    B       B_data_2
6    C       C_data_1

즉, 각 그룹의 마지막 레코드가 반환되어야합니다.

현재 이것은 내가 사용하는 쿼리입니다.

SELECT
  *
FROM (SELECT
  *
FROM messages
ORDER BY id DESC) AS x
GROUP BY name

그러나 이것은 매우 비효율적입니다. 동일한 결과를 얻는 다른 방법이 있습니까?


2
보다 효율적인 솔루션 은 stackoverflow.com/questions/1379565/… 에서 허용 된 답변을 참조하십시오
eyaler


7
DESC를 추가 할 수없는 이유는 다음과 같습니다. 즉, 메시지 그룹에서 *를 선택하십시오. 이름은 DESC
Kim Prince


2
@KimPrince 당신이 제안하는 답변이 예상대로 작동하지 않는 것 같습니다! 방금 방법을 시도해 보았고 각 그룹마다 첫 번째 행을 가져 와서 DESC를 주문했습니다. 각 그룹의 마지막 행을 차지하지 않습니다
Ayrat

답변:


967

MySQL 8.0은 이제 거의 모든 인기있는 SQL 구현과 같은 윈도우 기능을 지원합니다. 이 표준 구문을 사용하면 그룹당 최대 n 개의 쿼리를 작성할 수 있습니다.

WITH ranked_messages AS (
  SELECT m.*, ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC) AS rn
  FROM messages AS m
)
SELECT * FROM ranked_messages WHERE rn = 1;

아래는 2009 년에이 질문에 대해 작성한 최초의 답변입니다.


나는 이런 식으로 해결책을 씁니다.

SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
 ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;

성능과 관련하여 데이터의 특성에 따라 하나의 솔루션 또는 다른 솔루션이 더 나을 수 있습니다. 따라서 두 쿼리를 모두 테스트하고 데이터베이스에서 성능이 더 좋은 쿼리를 사용해야합니다.

예를 들어 StackOverflow August 데이터 덤프 복사본이 있습니다. 벤치마킹에 사용하겠습니다. Posts테이블 에는 1,114,357 개의 행이 있습니다 . Macbook Pro 2.40GHz의 MySQL 5.0.75에서 실행됩니다 .

주어진 사용자 ID (광산)에 대한 최신 게시물을 찾기 위해 쿼리를 작성합니다.

먼저 @Eric으로 표시된 기술을 GROUP BY하위 쿼리에서 사용하십시오.

SELECT p1.postid
FROM Posts p1
INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid
            FROM Posts pi GROUP BY pi.owneruserid) p2
  ON (p1.postid = p2.maxpostid)
WHERE p1.owneruserid = 20860;

1 row in set (1 min 17.89 sec)

EXPLAIN분석 조차도 16 초 이상이 걸립니다.

+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| id | select_type | table      | type   | possible_keys              | key         | key_len | ref          | rows    | Extra       |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                       | NULL        | NULL    | NULL         |   76756 |             | 
|  1 | PRIMARY     | p1         | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY     | 8       | p2.maxpostid |       1 | Using where | 
|  2 | DERIVED     | pi         | index  | NULL                       | OwnerUserId | 8       | NULL         | 1151268 | Using index | 
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
3 rows in set (16.09 sec)

이제 내 기술 을 사용하여 동일한 쿼리 결과를 생성하십시오 LEFT JOIN.

SELECT p1.postid
FROM Posts p1 LEFT JOIN posts p2
  ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid)
WHERE p2.postid IS NULL AND p1.owneruserid = 20860;

1 row in set (0.28 sec)

EXPLAIN두 테이블은 자신의 인덱스를 사용 할 수있는 분석 프로그램 :

+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| id | select_type | table | type | possible_keys              | key         | key_len | ref   | rows | Extra                                |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
|  1 | SIMPLE      | p1    | ref  | OwnerUserId                | OwnerUserId | 8       | const | 1384 | Using index                          | 
|  1 | SIMPLE      | p2    | ref  | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8       | const | 1384 | Using where; Using index; Not exists | 
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
2 rows in set (0.00 sec)

Posts테이블 의 DDL은 다음과 같습니다 .

CREATE TABLE `posts` (
  `PostId` bigint(20) unsigned NOT NULL auto_increment,
  `PostTypeId` bigint(20) unsigned NOT NULL,
  `AcceptedAnswerId` bigint(20) unsigned default NULL,
  `ParentId` bigint(20) unsigned default NULL,
  `CreationDate` datetime NOT NULL,
  `Score` int(11) NOT NULL default '0',
  `ViewCount` int(11) NOT NULL default '0',
  `Body` text NOT NULL,
  `OwnerUserId` bigint(20) unsigned NOT NULL,
  `OwnerDisplayName` varchar(40) default NULL,
  `LastEditorUserId` bigint(20) unsigned default NULL,
  `LastEditDate` datetime default NULL,
  `LastActivityDate` datetime default NULL,
  `Title` varchar(250) NOT NULL default '',
  `Tags` varchar(150) NOT NULL default '',
  `AnswerCount` int(11) NOT NULL default '0',
  `CommentCount` int(11) NOT NULL default '0',
  `FavoriteCount` int(11) NOT NULL default '0',
  `ClosedDate` datetime default NULL,
  PRIMARY KEY  (`PostId`),
  UNIQUE KEY `PostId` (`PostId`),
  KEY `PostTypeId` (`PostTypeId`),
  KEY `AcceptedAnswerId` (`AcceptedAnswerId`),
  KEY `OwnerUserId` (`OwnerUserId`),
  KEY `LastEditorUserId` (`LastEditorUserId`),
  KEY `ParentId` (`ParentId`),
  CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`)
) ENGINE=InnoDB;

8
정말? 많은 항목이 있으면 어떻게됩니까? 예를 들어, 사내 버전 컨트롤을 사용하여 작업하고 있고 파일 당 많은 버전이 있다면, 그 결합 결과는 엄청납니다. 이 방법으로 하위 쿼리 방법을 벤치마킹 한 적이 있습니까? 어느 쪽이 이길 지 궁금하지만 먼저 물어 보지 않을만큼 궁금합니다.
Eric

2
몇 가지 테스트를 수행했습니다. 작은 테이블 (~ 300k 레코드, ~ 190k 그룹, 대규모 그룹 등은 아님)에서 쿼리가 묶였습니다 (각 8 초).
Eric

1
@BillKarwin : 참조 meta.stackexchange.com/questions/123017 , 아담 Rackis '대답 아래, 특히 의견. 새 질문에 대한 답변을 되 찾으려면 알려주십시오.
Robert Harvey

3
<=고유하지 않은 열이 있으면 @Tim 이 도움이되지 않습니다. 순위 지정자로 고유 한 열을 사용해야합니다.
Bill Karwin

2
행 수가 증가하거나 그룹이 커지면 성능이 기하 급수적으로 저하됩니다. 예를 들어 5 개의 날짜로 구성된 그룹은 왼쪽 조인을 통해 4 + 3 + 2 + 1 + 1 = 11 개의 행을 생성하며 그 중 하나의 행이 결국 필터링됩니다. 그룹화 된 결과와의 결합 성능은 거의 선형입니다. 테스트에 결함이있는 것 같습니다.
살만 A

147

UPD : 2017-03-31, MySQL 버전 5.7.5 에서는 ONLY_FULL_GROUP_BY 스위치를 기본적으로 활성화했습니다 (따라서 비 결정적 GROUP BY 쿼리는 비활성화 됨). 또한 GROUP BY 구현을 업데이트했으며 비활성화 된 스위치로도 솔루션이 더 이상 예상대로 작동하지 않을 수 있습니다. 확인해야합니다.

작품 벌금 위 빌 Karwin의 솔루션은 그룹 내에서 항목 수는 오히려 작은 경우,하지만 그룹이 오히려 큰 경우 솔루션에 대한 요구하기 때문에 쿼리의 성능이 나쁜됩니다 n*n/2 + n/2만의 IS NULL비교.

그룹이 있는 InnoDB 테이블 18684446행 에서 테스트했습니다 1182. 이 표에는 기능 테스트에 대한 테스트 결과가 포함되어 (test_id, request_id)있으며 기본 키가 있습니다. 따라서 test_id그룹이며 나는 request_id각각에 대해 마지막 을 찾고있었습니다 test_id.

Bill의 솔루션은 이미 Dell e4310에서 몇 시간 동안 실행되어 왔으며 적용 지수 (따라서 using indexEXPLAIN)에서 작동하더라도 언제 완료 될지 알 수 없습니다 .

동일한 아이디어를 기반으로하는 다른 솔루션이 몇 가지 있습니다.

  • 기본 인덱스가 BTREE 인덱스 (일반적으로 경우) 인 경우 가장 큰 (group_id, item_value)쌍은 각 내에서 마지막 값입니다 . 인덱스를 내림차순으로 걸면 group_id첫 번째 값입니다 group_id.
  • 인덱스로 커버되는 값을 읽으면 인덱스 순서대로 값을 읽습니다.
  • 각 인덱스에는 암시 적으로 추가 된 기본 키 열이 포함됩니다 (즉, 기본 키는 적용 범위 인덱스에 있음). 아래 솔루션에서 기본 키에서 직접 작동하므로 결과에 기본 키 열을 추가하면됩니다.
  • 많은 경우 하위 쿼리에서 필요한 행 ID를 필요한 순서로 수집하고 하위 쿼리의 결과를 id에 결합하는 것이 훨씬 저렴합니다. 하위 쿼리 결과의 각 행에 대해 MySQL은 기본 키를 기반으로 한 단일 가져 오기가 필요하므로 하위 쿼리가 조인에 먼저 들어가고 하위 쿼리의 ID 순서대로 행이 출력됩니다 (명시 적 ORDER BY를 생략 한 경우) 가입)

MySQL이 인덱스를 사용하는 3 가지 방법 은 몇 가지 세부 사항을 이해하기위한 훌륭한 기사입니다.

해결책 1

이것은 엄청나게 빠르며 18M + 행에서 약 0.8 초가 걸립니다.

SELECT test_id, MAX(request_id) AS request_id
FROM testresults
GROUP BY test_id DESC;

순서를 ASC로 변경하려면 하위 쿼리에 넣고 ID 만 반환하고 하위 쿼리로 사용하여 나머지 열에 조인하십시오.

SELECT test_id, request_id
FROM (
    SELECT test_id, MAX(request_id) AS request_id
    FROM testresults
    GROUP BY test_id DESC) as ids
ORDER BY test_id;

이것은 내 데이터에 약 1.2 초가 걸립니다.

해결책 2

내 테이블에 약 19 초가 걸리는 다른 솔루션이 있습니다.

SELECT test_id, request_id
FROM testresults, (SELECT @group:=NULL) as init
WHERE IF(IFNULL(@group, -1)=@group:=test_id, 0, 1)
ORDER BY test_id DESC, request_id DESC

내림차순으로 테스트를 반환합니다. 전체 인덱스 스캔을 수행하기 때문에 속도가 훨씬 느리지 만 각 그룹에 대해 N 개의 최대 행을 출력하는 방법을 알려줍니다.

쿼리의 단점은 쿼리 캐시로 결과를 캐시 할 수 없다는 것입니다.


사람들이 자신의 플랫폼에서 테이블을 테스트 할 수 있도록 테이블 덤프에 연결하십시오.
Pacerier

3
해결 방법 1은 작동하지 않습니다. 그룹 별 절 없이는 request_id를 선택할 수 없습니다.
giò

2
@ giò,이 답변은 5 살입니다. MySQL은 5.7.5까지 ONLY_FULL_GROUP_BY은 기본적으로 비활성화 된이 솔루션은 상자 밖으로 일 dev.mysql.com/doc/relnotes/mysql/5.7/en/... . 이제 GROUP BY의 구현이 변경되었으므로 모드를 비활성화 할 때 솔루션이 여전히 작동하는지 확실하지 않습니다.
newtover 2016 년

첫 번째 솔루션에서 ASC를 원한다면 MAX를 MIN으로 설정하면 작동합니까?
Jin

@JinIzzraeel, 당신은 기본적으로 각 그룹의 최상위에 MIN이 있습니다 (커버링 인덱스의 순서입니다) : SELECT test_id, request_id FROM testresults GROUP BY test_id;각 test_id에 대한 최소 request_id를 반환합니다.
newtover

101

중간에 있으므로 하위 쿼리 를 사용하여 올바른 그룹화를 반환하십시오.

이 시도:

select
    a.*
from
    messages a
    inner join 
        (select name, max(id) as maxid from messages group by name) as b on
        a.id = b.maxid

그렇지 않으면 id최대 값을 원합니다.

select
    a.*
from
    messages a
    inner join 
        (select name, max(other_col) as other_col 
         from messages group by name) as b on
        a.name = b.name
        and a.other_col = b.other_col

이렇게하면 매우 느리거나 비효율적 인 하위 쿼리의 상관 된 하위 쿼리 및 / 또는 순서를 피할 수 있습니다.


1
와 솔루션에 대한주의 참고 other_col: 해당 열이 고유하지 않으면 당신은 여러 레코드가 동일한으로 백업받을 수 name그들이 묶을 경우 max(other_col). 내 요구에 대한 솔루션을 설명하는 이 게시물 을 찾았 습니다name .
Eric Simonton

경우에 따라이 솔루션 만 사용할 수 있지만 승인 된 솔루션은 사용할 수 없습니다.
tom10271

내 경험상 느리거나 비효율적 인 경향이 있는 전체 메시지 테이블그룹화합니다 ! 즉, 노트 서브 쿼리는 전체 테이블 스캔을 요구하는, 그리고 광산이 아니라고 당신의 최적화가 일을하지 않는 한 ... 부팅이에 그룹화를 수행합니다. 따라서이 솔루션은 전체 테이블을 메모리에 보관하는 데 크게 의존합니다.
Timo

사람들은 혜택을 누릴 것입니다 INDEX(name, id)INDEX(name, other_col)
릭 제임스

55

다른 솔루션에 도달했습니다. 각 그룹의 마지막 게시물에 대한 ID를 얻은 다음 첫 번째 쿼리의 결과를 WHERE x IN구문 의 인수로 사용하여 메시지 테이블에서 선택합니다 .

SELECT id, name, other_columns
FROM messages
WHERE id IN (
    SELECT MAX(id)
    FROM messages
    GROUP BY name
);

다른 솔루션과 비교하여 이것이 어떻게 수행되는지는 모르지만 3 백만 행 이상의 테이블에서 훌륭하게 작동했습니다. (1200+ 결과로 4 초 실행)

이것은 MySQL과 SQL Server 모두에서 작동합니다.


(이름, 아이디)에 색인이 있는지 확인하십시오.
Samuel Åslund

1
자기 조인이 훨씬 낫습니다
anwerj

나는 당신에게서 좋은
Humphrey

33

하위 쿼리 바이올린 링크에 의한 솔루션

select * from messages where id in
(select max(id) from messages group by Name)

솔루션 결합 조건 바이올린 링크

select m1.* from messages m1 
left outer join messages m2 
on ( m1.id<m2.id and m1.name=m2.name )
where m2.id is null

이 게시물의 이유는 바이올린 링크 만 제공하는 것입니다. 다른 답변에 동일한 SQL이 이미 제공되어 있습니다.


1
@AlexanderSuraphel mysql5.5는 현재 바이올린에서 사용할 수 없으며, 이것을 사용하여 바이올린 링크가 만들어졌습니다. 이제 일 바이올린은 mysql5.6을 지원하며 데이터베이스를 mysql 5.6으로 변경했으며 스키마를 작성하고 SQL을 실행할 수 있습니다.
Vipin

8

상당한 속도의 접근 방식은 다음과 같습니다.

SELECT * 
FROM messages a
WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)

결과

Id  Name    Other_Columns
3   A   A_data_3
5   B   B_data_2
6   C   C_data_1

이것은 id당신이 그것을 필요로하는 방식으로 주문되었다고 가정 합니다. 일반적인 경우 다른 열이 필요합니다.
Rick James

6

다음은 두 가지 제안입니다. 먼저, mysql이 ROW_NUMBER ()를 지원하면 매우 간단합니다.

WITH Ranked AS (
  SELECT Id, Name, OtherColumns,
    ROW_NUMBER() OVER (
      PARTITION BY Name
      ORDER BY Id DESC
    ) AS rk
  FROM messages
)
  SELECT Id, Name, OtherColumns
  FROM messages
  WHERE rk = 1;

나는 "마지막"이라고 가정하고 당신은 마지막으로 ID 순서를 의미합니다. 그렇지 않은 경우 ROW_NUMBER () 창의 ORDER BY 절을 적절히 변경하십시오. ROW_NUMBER ()를 사용할 수없는 경우 이것은 또 다른 해결책입니다.

둘째, 그렇지 않은 경우 다음과 같이 진행하는 것이 좋습니다.

SELECT
  Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
  SELECT * FROM messages as M2
  WHERE M2.Name = messages.Name
  AND M2.Id > messages.Id
)

즉, 같은 이름을 가진 나중의 ID 메시지가없는 메시지를 선택하십시오.


8
MySQL은 ROW_NUMBER () 또는 CTE를 지원하지 않습니다.
Bill Karwin

1
MySQL 8.0 (및 MariaDB 10.2)은 이제 ROW_NUMBER()CTE를 지원 합니다.
Rick James

6

아직 큰 DB로 테스트하지는 않았지만 테이블을 조인하는 것보다 빠를 수 있다고 생각합니다.

SELECT *, Max(Id) FROM messages GROUP BY Name

14
임의의 데이터를 반환합니다. 즉, 반환 된 열이 MAX (Id) 인 레코드에 없을 수 있습니다.
피해

WHERE 조건의 레코드 세트에서 최대 ID를 선택하는 데 유용합니다. "SELECT Max (Id) FROM Prod WHERE Pn = '"+ Pn + "'"동일한 Pn을 가진 레코드 세트에서 최대 ID를 리턴합니다. reader.GetString (0)을 사용하여 결과를 얻으십시오
Nicola

5

다음은 GROUP_CONCATorder by를 사용하여 마지막 관련 레코드를 가져 오고 SUBSTRING_INDEX목록에서 레코드 중 하나를 선택하는 다른 방법입니다.

SELECT 
  `Id`,
  `Name`,
  SUBSTRING_INDEX(
    GROUP_CONCAT(
      `Other_Columns` 
      ORDER BY `Id` DESC 
      SEPARATOR '||'
    ),
    '||',
    1
  ) Other_Columns 
FROM
  messages 
GROUP BY `Name` 

위의 쿼리는 Other_Columns동일한 Name그룹 에 있는 모든 것을 그룹화하고 사용 ORDER BY id DESC하면 Other_Columns내 경우에 제공된 구분 기호를 사용하여 특정 그룹의 모든 항목 을 내림차순으로 내림차순으로 결합합니다. 이 목록을 ||사용 SUBSTRING_INDEX하면 첫 번째 항목을 선택합니다

바이올린 데모


group_concat_max_len처리 할 수있는 행 수 를 제한합니다.
릭 제임스

5

분명히 동일한 결과를 얻는 데는 여러 가지 방법이 있습니다. 귀하의 질문은 MySQL의 각 그룹에서 마지막 결과를 얻는 효율적인 방법 인 것 같습니다. 방대한 양의 데이터로 작업하고 있고 최신 버전의 MySQL (예 : 5.7.21 및 8.0.4-rc)에서도 InnoDB를 사용한다고 가정하면이 작업을 효율적으로 수행 할 수있는 방법이 없을 수 있습니다.

때로는 6000 만 개가 넘는 행이있는 테이블을 사용하여이 작업을 수행해야합니다.

이 예제에서는 쿼리에서 데이터의 모든 그룹에 대한 결과를 찾아야하는 행이 약 150 만 행인 데이터를 사용합니다. 실제 사례에서 우리는 종종 약 2,000 개의 그룹에서 데이터를 반환해야합니다 (가설로 많은 데이터를 검사 할 필요가 없음).

다음 테이블을 사용합니다.

CREATE TABLE temperature(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT, 
  groupID INT UNSIGNED NOT NULL, 
  recordedTimestamp TIMESTAMP NOT NULL, 
  recordedValue INT NOT NULL,
  INDEX groupIndex(groupID, recordedTimestamp), 
  PRIMARY KEY (id)
);

CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id)); 

온도 표는 약 150 만 개의 무작위 레코드와 100 개의 다른 그룹으로 채워져 있습니다. selected_group은 100 개의 그룹으로 채워집니다 (이 경우 일반적으로 모든 그룹에서 20 % 미만입니다).

이 데이터는 임의이므로 여러 행에 동일한 recordedTimestamp가있을 수 있습니다. 우리가 원하는 것은 각 그룹에 대해 마지막으로 기록 된 타임 스탬프를 가진 groupID 순서대로 선택한 모든 그룹의 목록을 얻는 것입니다. 같은 그룹에 일치하는 행이 두 개 이상 있으면 해당 행의 마지막 일치하는 ID가 있습니다.

가상의 MySQL에 특별한 ORDER BY 절의 마지막 행에서 값을 반환하는 last () 함수가 있다면 간단히 다음과 같이 할 수 있습니다.

SELECT 
  last(t1.id) AS id, 
  t1.groupID, 
  last(t1.recordedTimestamp) AS recordedTimestamp, 
  last(t1.recordedValue) AS recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
ORDER BY t1.recordedTimestamp, t1.id
GROUP BY t1.groupID;

이 경우 일반적인 GROUP BY 함수를 사용하지 않으므로이 경우 몇 개의 100 개 행만 검사하면됩니다. 이것은 0 초 안에 실행되므로 매우 효율적입니다. 일반적으로 MySQL에서는 GROUP BY 절 뒤에 ORDER BY 절이 표시되지만이 ORDER BY 절은 last () 함수의 ORDER를 결정하는 데 사용됩니다. GROUP BY 이후 인 경우 GROUPS를 주문합니다. GROUP BY 절이 없으면 마지막 값은 반환 된 모든 행에서 동일합니다.

그러나 MySQL에는 이것이 없기 때문에 그것이 가지고있는 것에 대한 다른 아이디어를 살펴보고 이것들 중 어느 것도 효율적이지 않다는 것을 증명합시다.

실시 예 1

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
  SELECT t2.id
  FROM temperature t2 
  WHERE t2.groupID = g.id
  ORDER BY t2.recordedTimestamp DESC, t2.id DESC
  LIMIT 1
);

이것은 3,009,254 행을 검사하고 5.7.21에서 ~ 0.859 초가 걸리고 8.0.4-rc에서 약간 더 길었습니다.

실시 예 2

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM temperature t1
INNER JOIN ( 
  SELECT max(t2.id) AS id   
  FROM temperature t2
  INNER JOIN (
    SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
    FROM selected_group g
    INNER JOIN temperature t3 ON t3.groupID = g.id
    GROUP BY t3.groupID
  ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
  GROUP BY t2.groupID
) t5 ON t5.id = t1.id;

이것은 1,505,331 행을 검사하고 5.7.21에서 ~ 1.25 초가 걸리고 8.0.4-rc에서 약간 더 길었습니다.

실시 예 3

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM temperature t1
WHERE t1.id IN ( 
  SELECT max(t2.id) AS id   
  FROM temperature t2
  INNER JOIN (
    SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
    FROM selected_group g
    INNER JOIN temperature t3 ON t3.groupID = g.id
    GROUP BY t3.groupID
  ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
  GROUP BY t2.groupID
)
ORDER BY t1.groupID;

이것은 3,009,685 행을 검사하고 5.7.21에서 ~ 1.95 초가 걸리고 8.0.4-rc에서 약간 더 길었습니다.

실시 예 4

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
  SELECT max(t2.id)
  FROM temperature t2 
  WHERE t2.groupID = g.id AND t2.recordedTimestamp = (
      SELECT max(t3.recordedTimestamp)
      FROM temperature t3 
      WHERE t3.groupID = g.id
    )
);

이것은 6,137,810 행을 검사하고 5.7.21에서 ~ 2.2 초가 걸리고 8.0.4-rc에서 약간 더 길었습니다.

실시 예 5

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
  SELECT 
    t2.id, 
    t2.groupID, 
    t2.recordedTimestamp, 
    t2.recordedValue, 
    row_number() OVER (
      PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC
    ) AS rowNumber
  FROM selected_group g 
  INNER JOIN temperature t2 ON t2.groupID = g.id
) t1 WHERE t1.rowNumber = 1;

이것은 6,017,808 행을 검사하고 8.0.4-rc에서 ~ 4.2 초가 걸렸습니다.

실시 예 6

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM (
  SELECT 
    last_value(t2.id) OVER w AS id, 
    t2.groupID, 
    last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp, 
    last_value(t2.recordedValue) OVER w AS recordedValue
  FROM selected_group g
  INNER JOIN temperature t2 ON t2.groupID = g.id
  WINDOW w AS (
    PARTITION BY t2.groupID 
    ORDER BY t2.recordedTimestamp, t2.id 
    RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  )
) t1
GROUP BY t1.groupID;

이것은 6,017,908 행을 검사하고 8.0.4-rc에서 ~ 17.5 초가 걸렸습니다.

실시 예 7

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
LEFT JOIN temperature t2 
  ON t2.groupID = g.id 
  AND (
    t2.recordedTimestamp > t1.recordedTimestamp 
    OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id)
  )
WHERE t2.id IS NULL
ORDER BY t1.groupID;

이건 영원히 복용해서 죽여야 했어요


이것은 다른 문제입니다. 그리고 솔루션은 거대한 UNION ALL 쿼리입니다.
Paul Spiegel 2014

@PaulSpiegel 나는 당신이 거대한 UNION ALL에 대해 농담하고 있다고 생각합니다. 선택한 모든 그룹을 미리 알아야하고 엄청나게 큰 쿼리 인 2,000 개의 선택된 그룹을 사용하면 위의 가장 빠른 예보다 성능이 더 나빠질 것입니다. 해결책.
Yoseph

나는 절대적으로 심각하다. 나는 과거에 몇 백 그룹으로 테스트했습니다. 대규모 그룹의 관계를 처리해야하는 경우 MySQL에서 UNION ALL을 사용하여 최적의 실행 계획을 강요 할 수 있습니다. SELECT DISTINCT(groupID)빠르며 이러한 쿼리를 구성하는 데 필요한 모든 데이터를 제공합니다. max_allowed_packetMySQL 5.7에서는 기본값이 4MB를 초과하지 않는 한 쿼리 크기가 양호해야합니다 .
Paul Spiegel

5

Group By of 레코드에서 마지막 레코드를 가져올 때 MySQL을 사용하는 방법을 살펴 보겠습니다. 예를 들어이 결과 게시물 세트가있는 경우.

id category_id post_title

1 1 Title 1

2 1 Title 2

3 1 Title 3

4 2 Title 4

5 2 Title 5

6 3 Title 6

제목 3, 제목 5 및 제목 6 인 각 범주에서 마지막 게시물을 얻을 수 있기를 원합니다. 범주별로 게시물을 가져 오려면 MySQL Group By 키보드를 사용합니다.

select * from posts group by category_id

그러나이 쿼리에서 얻은 결과는 다음과 같습니다.

id category_id post_title

1 1 Title 1

4 2 Title 4

6 3 Title 6

그룹 기준은 항상 결과 집합에서 그룹의 첫 번째 레코드를 반환합니다.

SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id );

각 그룹에서 ID가 가장 높은 게시물을 반환합니다.

id category_id post_title

3 1 Title 3

5 2 Title 5

6 3 Title 6

참조 여기를 클릭하십시오


4
SELECT 
  column1,
  column2 
FROM
  table_name 
WHERE id IN 
  (SELECT 
    MAX(id) 
  FROM
    table_name 
  GROUP BY column1) 
ORDER BY column1 ;

답을 좀 더 자세히 설명해 주시겠습니까? 검색어가 Vijays 원래 검색어보다 선호되는 이유는 무엇입니까?
janfoeh

4

내 해결책은 다음과 같습니다.

SELECT 
  DISTINCT NAME,
  MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES 
FROM MESSAGE;

이름별로 최신 메시지를 반환하지 않습니다. 그리고 그것은 단지 너무 복잡한 버전입니다 SELECT NAME, MAX(MESSAGES) MESSAGES FROM MESSAGE GROUP BY NAME.
Paul Spiegel 2014

또한,이 제형은 매우 비효율적이다.
Rick James

3

이 시도:

SELECT jos_categories.title AS name,
       joined .catid,
       joined .title,
       joined .introtext
FROM   jos_categories
       INNER JOIN (SELECT *
                   FROM   (SELECT `title`,
                                  catid,
                                  `created`,
                                  introtext
                           FROM   `jos_content`
                           WHERE  `sectionid` = 6
                           ORDER  BY `id` DESC) AS yes
                   GROUP  BY `yes`.`catid` DESC
                   ORDER  BY `yes`.`created` DESC) AS joined
         ON( joined.catid = jos_categories.id )  

3

테이블 메시지 에 자동 증분 기본 키인 Id 가 포함 된 경우 @Vijay Dev에서 안녕하세요. 그러면 기본 키를 기준으로 최신 레코드 기반을 가져와 쿼리에서 다음과 같이 읽어야합니다.

SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId

이것은 내가 찾은 가장 빠른 것
CORSAIR

3

여기서도 볼 수 있습니다.

http://sqlfiddle.com/#!9/ef42b/9

첫 번째 솔루션

SELECT d1.ID,Name,City FROM Demo_User d1
INNER JOIN
(SELECT MAX(ID) AS ID FROM Demo_User GROUP By NAME) AS P ON (d1.ID=P.ID);

두 번째 솔루션

SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;

3
SELECT * FROM table_name WHERE primary_key IN (SELECT MAX(primary_key) FROM table_name GROUP BY column_name )

3

**

안녕하세요,이 쿼리가 도움이 될 수 있습니다 :

**

SELECT 
  *
FROM 
  message 

WHERE 
  `Id` IN (
    SELECT 
      MAX(`Id`) 
    FROM 
      message 
    GROUP BY 
      `Name`
  ) 
ORDER BY 
   `Id` DESC

2

이 방법을 사용하여 테이블에서 중복을 삭제할 수있는 방법이 있습니까? 결과 집합은 기본적으로 고유 한 레코드 모음이므로 결과 집합에없는 모든 레코드를 삭제할 수 있다면 사실상 중복이 없습니까? 나는 이것을 시도했지만 mySQL은 1093 오류를 주었다.

DELETE FROM messages WHERE id NOT IN
 (SELECT m1.id  
 FROM messages m1 LEFT JOIN messages m2  
 ON (m1.name = m2.name AND m1.id < m2.id)  
 WHERE m2.id IS NULL)

출력을 임시 변수에 저장 한 다음 NOT IN (임시 변수)에서 삭제하는 방법이 있습니까? @Bill은 매우 유용한 솔루션에 감사드립니다.

편집 : 해결책을 찾았습니다 생각 :

DROP TABLE IF EXISTS UniqueIDs; 
CREATE Temporary table UniqueIDs (id Int(11)); 

INSERT INTO UniqueIDs 
    (SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON 
    (T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields  
    AND T1.ID < T2.ID) 
    WHERE T2.ID IS NULL); 

DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);

2

아래 쿼리는 귀하의 질문에 따라 잘 작동합니다.

SELECT M1.* 
FROM MESSAGES M1,
(
 SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data
 FROM MESSAGES
 GROUP BY 1
) M2
WHERE M1.Others_data = M2.Max_Others_data
ORDER BY Others_data;

2

각의 마지막 행을 원하면 내림차순으로 및 Name행을 기준으로 각 행 그룹에 행 번호를 제공 할 수 있습니다 .NameId

질문

SELECT t1.Id, 
       t1.Name, 
       t1.Other_Columns
FROM 
(
     SELECT Id, 
            Name, 
            Other_Columns,
    (
        CASE Name WHEN @curA 
        THEN @curRow := @curRow + 1 
        ELSE @curRow := 1 AND @curA := Name END 
    ) + 1 AS rn 
    FROM messages t, 
    (SELECT @curRow := 0, @curA := '') r 
    ORDER BY Name,Id DESC 
)t1
WHERE t1.rn = 1
ORDER BY t1.Id;

SQL 바이올린


2

이건 어때요:

SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;

비슷한 문제 (postgresql tough)와 1M 레코드 테이블에 있습니다. 이 솔루션은 LEFT JOIN을 사용하여 1.7 초 대 44 초가 걸립니다. 필자의 경우 이름 필드의 해당 값을 NULL 값 으로 필터링하여 0.2 초 더 나은 성능을 얻었습니다.


1

성능이 실제로 우려 IsLastInGroup되는 경우 BIT 유형 이라는 테이블에 새 열을 도입 할 수 있습니다 .

마지막 열에서는 true로 설정하고 모든 행 삽입 / 업데이트 / 삭제마다 유지하십시오. 쓰기 속도는 느리지 만 읽기에 도움이됩니다. 사용 사례에 따라 다르며 읽기 중심 인 경우에만 권장합니다.

따라서 쿼리는 다음과 같습니다.

SELECT * FROM Messages WHERE IsLastInGroup = 1

무들의 일부 테이블에는 이와 같은 플래그 열이 있습니다.
Lawrence


0

계산하여 그룹화하고 다음과 같이 그룹의 마지막 항목을 가져올 수도 있습니다.

SELECT 
    user,
    COUNT(user) AS count,
    MAX(id) as last
FROM request 
GROUP BY user

0

오라클 쿼리 아래에 도움이 될 수 있습니다.

WITH Temp_table AS
(
    Select id, name, othercolumns, ROW_NUMBER() over (PARTITION BY name ORDER BY ID 
    desc)as rank from messages
)
Select id, name,othercolumns from Temp_table where rank=1

0

다른 접근법 :

각 프로그램과 함께 최대 m2_price로 속성을 찾으십시오 (1 개의 프로그램에서 n 개의 속성).

select * from properties p
join (
    select max(m2_price) as max_price 
    from properties 
    group by program_id
) p2 on (p.program_id = p2.program_id)
having p.m2_price = max_price
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.