[mysql] SQL의 다른 열에 의해 MAX (열 값), DISTINCT를 사용하여 행을 어떻게 선택할 수 있습니까?

내 테이블은 :

id  home  datetime     player   resource
---|-----|------------|--------|---------
1  | 10  | 04/03/2009 | john   | 399 
2  | 11  | 04/03/2009 | juliet | 244
5  | 12  | 04/03/2009 | borat  | 555
3  | 10  | 03/03/2009 | john   | 300
4  | 11  | 03/03/2009 | juliet | 200
6  | 12  | 03/03/2009 | borat  | 500
7  | 13  | 24/12/2008 | borat  | 600
8  | 13  | 01/01/2009 | borat  | 700

home최대 값을 보유한 각 고유 항목을 선택해야합니다 datetime.

결과는 다음과 같습니다.

id  home  datetime     player   resource 
---|-----|------------|--------|---------
1  | 10  | 04/03/2009 | john   | 399
2  | 11  | 04/03/2009 | juliet | 244
5  | 12  | 04/03/2009 | borat  | 555
8  | 13  | 01/01/2009 | borat  | 700

나는 시도했다 :

-- 1 ..by the MySQL manual: 

SELECT DISTINCT
  home,
  id,
  datetime AS dt,
  player,
  resource
FROM topten t1
WHERE datetime = (SELECT
  MAX(t2.datetime)
FROM topten t2
GROUP BY home)
GROUP BY datetime
ORDER BY datetime DESC

작동하지 않습니다. 데이터베이스에 187 개가 있지만 결과 집합에 130 개의 행이 있습니다. 결과에의 일부 중복이 포함됩니다 home.

-- 2 ..join

SELECT
  s1.id,
  s1.home,
  s1.datetime,
  s1.player,
  s1.resource
FROM topten s1
JOIN (SELECT
  id,
  MAX(datetime) AS dt
FROM topten
GROUP BY id) AS s2
  ON s1.id = s2.id
ORDER BY datetime 

아니. 모든 기록을 제공합니다.

-- 3 ..something exotic: 

다양한 결과로.



답변

당신은 너무 가깝습니다! 집과 최대 날짜 시간 topten을 모두 선택한 다음 BOTH 필드 의 테이블에 다시 가입하기 만하면됩니다.

SELECT tt.*
FROM topten tt
INNER JOIN
    (SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home) groupedtt 
ON tt.home = groupedtt.home 
AND tt.datetime = groupedtt.MaxDateTime


답변

MySQL내부 쿼리가없고없는 가장 빠른 솔루션 GROUP BY:

SELECT m.*                    -- get the row that contains the max value
FROM topten m                 -- "m" from "max"
    LEFT JOIN topten b        -- "b" from "bigger"
        ON m.home = b.home    -- match "max" row with "bigger" row by `home`
        AND m.datetime < b.datetime           -- want "bigger" than "max"
WHERE b.datetime IS NULL      -- keep only if there is no bigger than max

설명 :

home열을 사용하여 테이블 자체를 조인하십시오 . 를 사용 LEFT JOIN하면 테이블의 모든 행 m이 결과 집합에 나타납니다. 테이블에서 일치하지 않는 항목은 의 열에 대해을 b갖습니다 .NULLb

의 다른 조건은의 JOIN행 보다 열의 b값이 큰 행만 일치하도록 요청합니다 .datetimem

질문에 게시 된 데이터를 사용하면 다음과 LEFT JOIN같은 쌍이 생성됩니다.

+------------------------------------------+--------------------------------+
|              the row from `m`            |    the matching row from `b`   |
|------------------------------------------|--------------------------------|
| id  home  datetime     player   resource | id    home   datetime      ... |
|----|-----|------------|--------|---------|------|------|------------|-----|
| 1  | 10  | 04/03/2009 | john   | 399     | NULL | NULL | NULL       | ... | *
| 2  | 11  | 04/03/2009 | juliet | 244     | NULL | NULL | NULL       | ... | *
| 5  | 12  | 04/03/2009 | borat  | 555     | NULL | NULL | NULL       | ... | *
| 3  | 10  | 03/03/2009 | john   | 300     | 1    | 10   | 04/03/2009 | ... |
| 4  | 11  | 03/03/2009 | juliet | 200     | 2    | 11   | 04/03/2009 | ... |
| 6  | 12  | 03/03/2009 | borat  | 500     | 5    | 12   | 04/03/2009 | ... |
| 7  | 13  | 24/12/2008 | borat  | 600     | 8    | 13   | 01/01/2009 | ... |
| 8  | 13  | 01/01/2009 | borat  | 700     | NULL | NULL | NULL       | ... | *
+------------------------------------------+--------------------------------+

마지막 WHERE으로이 절 NULL은 열에 s 가있는 쌍만 유지합니다 b( *위의 표에 표시되어 있음 ). 이는 JOIN절의 두 번째 조건으로 인해에서 선택된 행 m이 column에서 가장 큰 값을 갖음을 의미 datetime합니다.

다른 SQL 팁에 대해서는 SQL 안티 패턴 : 데이터베이스 프로그래밍의 함정 피하기 책을 읽으십시오 .


답변

다음은 T-SQL 버전입니다.

-- Test data
DECLARE @TestTable TABLE (id INT, home INT, date DATETIME, 
  player VARCHAR(20), resource INT)
INSERT INTO @TestTable
SELECT 1, 10, '2009-03-04', 'john', 399 UNION
SELECT 2, 11, '2009-03-04', 'juliet', 244 UNION
SELECT 5, 12, '2009-03-04', 'borat', 555 UNION
SELECT 3, 10, '2009-03-03', 'john', 300 UNION
SELECT 4, 11, '2009-03-03', 'juliet', 200 UNION
SELECT 6, 12, '2009-03-03', 'borat', 500 UNION
SELECT 7, 13, '2008-12-24', 'borat', 600 UNION
SELECT 8, 13, '2009-01-01', 'borat', 700

-- Answer
SELECT id, home, date, player, resource 
FROM (SELECT id, home, date, player, resource, 
    RANK() OVER (PARTITION BY home ORDER BY date DESC) N
    FROM @TestTable
)M WHERE N = 1

-- and if you really want only home with max date
SELECT T.id, T.home, T.date, T.player, T.resource 
    FROM @TestTable T
INNER JOIN 
(   SELECT TI.id, TI.home, TI.date, 
        RANK() OVER (PARTITION BY TI.home ORDER BY TI.date) N
    FROM @TestTable TI
    WHERE TI.date IN (SELECT MAX(TM.date) FROM @TestTable TM)
)TJ ON TJ.N = 1 AND T.id = TJ.id

편집
불행히도 MySQL에는 RANK () OVER 함수가 없습니다.
그러나 에뮬레이션 할 수 있습니다 (MySQL로 분석 (AKA 순위) 함수 에뮬레이션 참조 ) .
따라서 이것은 MySQL 버전입니다.

SELECT id, home, date, player, resource 
FROM TestTable AS t1 
WHERE 
    (SELECT COUNT(*) 
            FROM TestTable AS t2 
            WHERE t2.home = t1.home AND t2.date > t1.date
    ) = 0


답변

이것은 당신이 각각 두 개 이상의 행이있을 경우에도 작동합니다 home동일한와 DATETIME의 :

SELECT id, home, datetime, player, resource
FROM   (
       SELECT (
              SELECT  id
              FROM    topten ti
              WHERE   ti.home = t1.home
              ORDER BY
                      ti.datetime DESC
              LIMIT 1
              ) lid
       FROM   (
              SELECT  DISTINCT home
              FROM    topten
              ) t1
       ) ro, topten t2
WHERE  t2.id = ro.lid


답변

나는 이것이 당신에게 원하는 결과를 줄 것이라고 생각합니다 :

SELECT   home, MAX(datetime)
FROM     my_table
GROUP BY home

그러나 다른 열도 필요하면 원래 테이블과 조인하십시오 ( Michael La Voie답변 확인 ).

친애하는.


답변

사람들 이이 스레드를 계속 실행하는 것처럼 보이기 때문에 (댓글 날짜 범위는 1.5 년입니다) 그리 간단하지 않습니다.

SELECT * FROM (SELECT * FROM topten ORDER BY datetime DESC) tmp GROUP BY home

집계 함수가 필요하지 않습니다 …

건배.


답변

이것을 시도해 볼 수 있으며 큰 테이블의 경우 쿼리 성능이 향상됩니다. 각 가정에 대해 두 개 이상의 레코드가없고 날짜가 다른 경우 작동합니다. 더 나은 일반적인 MySQL 쿼리는 위의 Michael La Voie의 쿼리입니다.

SELECT t1.id, t1.home, t1.date, t1.player, t1.resource
FROM   t_scores_1 t1 
INNER JOIN t_scores_1 t2
   ON t1.home = t2.home
WHERE t1.date > t2.date

또는 Postgres 또는 분석 기능을 제공하는 DB의 경우

SELECT t.* FROM 
(SELECT t1.id, t1.home, t1.date, t1.player, t1.resource
  , row_number() over (partition by t1.home order by t1.date desc) rw
 FROM   topten t1 
 INNER JOIN topten t2
   ON t1.home = t2.home
 WHERE t1.date > t2.date 
) t
WHERE t.rw = 1