SQLの別の列でMAX(列値)、DISTINCTを含む行をどのように選択できますか?


768

私のテーブルは:

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: 

さまざまな結果が得られます。

回答:


939

あなたはとても近いです!あなたがする必要があるのは、家とその最大日付時間の両方を選択し、次にtopten両方のフィールドでテーブルに参加することです:

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

5
2つの等しい最大日時が同じ家にいる場合(異なるプレーヤーで)、それが異なるかどうかをテストします
Maksym Gontar

5
これを行う古典的な方法は、自然な結合によるものだと思います。「SELECT tt。* FROM topten tt NATURAL JOIN(SELECT home、MAX(datetime)AS datetime FROM topten GROUP BY home)mostrecent;」同じクエリですが、間違いなく読みやすくなっています
Parker

32
同じ「home」と「datetime」フィールド値を持つ2つの行がある場合はどうでしょうか?
Kemal Duran

3
@Youngクエリの問題は、が返される可能性があることidplayerおよびresource特定のホームの最大でない行(つまり、home = 10の場合)が得られることです。3 | 10 | 04/03/2009 | john | 300 つまり、結果セットの行のすべての列が所属することを保証するものではありません。与えられた家のmax(datetime)に。
sactiw 2015年

1
クエリの@ me1111問題は、指定された家のmax(datetime)番目の行を返す場合と返さない場合があります。GROUP BYである理由は、ホームごとにランダムな行をフェッチし、ORDER BYは、GROUP BYによって生成されたすべての結果全体をソートするだけです
sactiw

87

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持ちNULLますb

上の他の条件は、JOIN唯一の行と一致するように依頼bに大きい値有するdatetimeから行よりも列m

質問に投稿されたデータを使用して、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句の2番目の条件により、から選択された行mの最大値はcolumnになりますdatetime

その他のSQLのヒントについては、SQLアンチパターン:データベースプログラミングの落とし穴を回避するをお読みください。


を使用するSQLiteと、一致する列にインデックスがない場合(つまり "home")、最初のバージョンはLa Voieのバージョンよりもはるかに遅くなります。(
24,000

10
これが最良の答えです。実行プランを表示すると、このクエリでは1ステップ少なくなります
TlmaK0

2行が同じhomedatetimeあり、それdatetimeがその特定の最大値である場合はhomeどうなりますか?
Istiaque Ahmed 2017

両方の行が結果セットに表示されます。この答えは概念実証です。実際のコードでは、おそらくこの状況でそれらの1つだけを選択する別の基準があります(おそらく最初のものか最後のものか、または別の列を使用して決定します)。この基準をON句の新しい条件として追加するだけです。Feが... ON ... AND m.id < b.id最新のエントリ(最大で1を維持するためにid2つの行が同じで値を持っている場合)homedatetime、列を、それが最大ですdatetime
axiac 2017年

このようなクエリに最適なインデックスはどれですか?
AjaxLeung

73

ここに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

#1064-SQL構文にエラーがあります。MySQLサーバーバージョンに対応するマニュアルで、near '()OVER(PARTITION BY krd ORDER BY daytime DESC)N FROM @rapsa)M WHERE N =' at line 1
Kaptah

2
ああ、それであなたはMySQLを使っています。そこから始めましょう!すぐに回答を更新します。
Maksym Gontar 2009年

@MaxGontar、あなたのmysqlソリューションは素晴らしいです。@_TestTableでrow#1>を削除するとどうなりますか:SELECT 1、10、 '2009-03-04'、 'john'、399、これは、特定の家の値に対して単一の行がある場合はどうなるのでしょうか。THX。
egidiocs

2
バグ:「RANK()」を「ROW_NUMBER()」に置き換えます。あなたは(重複日付値によって引き起こされる)のネクタイを持っている場合は、N.のために「1」を持つ2つのレコードを持つことになります
MikeTeeVee

29

これはhome、等しいDATETIMEの行が2行以上ある場合でも機能します。

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

表にふたのフィールドを追加しました。
ノーグッド

1
これはPHPMyAdminでは実行されませんでした。ページは更新されますが、結果もエラーもありません。
Kaptah 2009年

WHERE ti.home = t1.home-構文を説明できますか?
Istiaque Ahmed 2017

@IstiaqueAhmed:ここで理解できないことは何ですか?これは相関クエリであり、言及する式は相関条件です。
Quassnoi

@Quassnoi、select行を持つクエリには、を定義WHERE ti.home = t1.home するFROM句は必要ありませんt1。それで、それはどのように使用されますか?
イスティアケアーメド2017

26

私はこれがあなたに望ましい結果を与えると思います:

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

しかし、あなたは同様に他の列が必要な場合は、単に元のテーブル(チェックと結合させるMichael La Voieの答え)

宜しくお願いします。


8
他の列も必要です。
Quassnoi 2009年

4
id、home、datetime、player、resource
Quassnoi 2009年

17

人々はこのスレッド(コメントの日付範囲が1.5年)に出くわすように見えるので、それほど単純ではありません。

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

集約関数は必要ありません...

乾杯。


6
これは機能していないようです。エラーメッセージ:列 'x'は集計関数またはGROUP BY句のいずれにも含まれていないため、選択リストでは無効です。
鶏は

これはSQL ServerやOracleでは機能しませんが、MySQLでは機能するようです。
ErikE 2014年

これは本当に美しいです!これはどのように作動しますか?DESCとデフォルトのグループリターン列を使用して?それで、日付時刻ASCに変更した場合、各家の最も古い行が返されますか?
wayofthefuture 2016年

これは素晴らしいです!
犬の恋人

非集計列(MySQLの場合)がある場合、この調整は機能しません。
user3562927 2017

11

これを試すこともでき、テーブルが大きい場合はクエリのパフォーマンスが向上します。各家のレコードが2つ以下で、日付が異なる場合に機能します。より良い一般的な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

この答えは正しいですか?私はそれを使おうとしましたが、 'home'の最新の日付のレコードを選択するのではなく、最も古い日付のレコードのみを削除します。次に例を示します。SQLfiddle
marcin93w

1
@kidOfDeath-返信をコンテキストとPostgresクエリで更新
Shiva

を使用するSQLiteと、一致する列にインデックスがない場合(つまり "home")、最初のバージョンはLa Voieのバージョンよりもはるかに遅くなります。
Thomas Tempelmann、2015

8

これはOracleで機能します。

with table_max as(
  select id
       , home
       , datetime
       , player
       , resource
       , max(home) over (partition by home) maxhome
    from table  
)
select id
     , home
     , datetime
     , player
     , resource
  from table_max
 where home = maxhome

1
これは最大の日時をどのように選ぶのですか?彼は家ごとにグループ化し、最大日時を選択するように求めました。これがどのように行われるかはわかりません。
n00b 2013

8
SELECT  tt.*
FROM    TestTable tt 
INNER JOIN 
        (
        SELECT  coord, MAX(datetime) AS MaxDateTime 
        FROM    rapsa 
        GROUP BY
                krd 
        ) groupedtt
ON      tt.coord = groupedtt.coord
        AND tt.datetime = groupedtt.MaxDateTime

8

SQL Serverでこれを試してください:

WITH cte AS (
   SELECT home, MAX(year) AS year FROM Table1 GROUP BY home
)
SELECT * FROM Table1 a INNER JOIN cte ON a.home = cte.home AND a.year = cte.year

5
SELECT c1, c2, c3, c4, c5 FROM table1 WHERE c3 = (select max(c3) from table)

SELECT * FROM table1 WHERE c3 = (select max(c3) from table1)

5

これは、グループに重複するMAX(datetime)がある1つのエントリのみを出力するMySQLバージョンです。

ここでテストできますhttp://www.sqlfiddle.com/#!2/0a4ae/1

サンプルデータ

mysql> SELECT * from topten;
+------+------+---------------------+--------+----------+
| id   | home | datetime            | player | resource |
+------+------+---------------------+--------+----------+
|    1 |   10 | 2009-04-03 00:00:00 | john   |      399 |
|    2 |   11 | 2009-04-03 00:00:00 | juliet |      244 |
|    3 |   10 | 2009-03-03 00:00:00 | john   |      300 |
|    4 |   11 | 2009-03-03 00:00:00 | juliet |      200 |
|    5 |   12 | 2009-04-03 00:00:00 | borat  |      555 |
|    6 |   12 | 2009-03-03 00:00:00 | borat  |      500 |
|    7 |   13 | 2008-12-24 00:00:00 | borat  |      600 |
|    8 |   13 | 2009-01-01 00:00:00 | borat  |      700 |
|    9 |   10 | 2009-04-03 00:00:00 | borat  |      700 |
|   10 |   11 | 2009-04-03 00:00:00 | borat  |      700 |
|   12 |   12 | 2009-04-03 00:00:00 | borat  |      700 |
+------+------+---------------------+--------+----------+

ユーザー変数を含むMySQLバージョン

SELECT *
FROM (
    SELECT ord.*,
        IF (@prev_home = ord.home, 0, 1) AS is_first_appear,
        @prev_home := ord.home
    FROM (
        SELECT t1.id, t1.home, t1.player, t1.resource
        FROM topten t1
        INNER JOIN (
            SELECT home, MAX(datetime) AS mx_dt
            FROM topten
            GROUP BY home
          ) x ON t1.home = x.home AND t1.datetime = x.mx_dt
        ORDER BY home
    ) ord, (SELECT @prev_home := 0, @seq := 0) init
) y
WHERE is_first_appear = 1;
+------+------+--------+----------+-----------------+------------------------+
| id   | home | player | resource | is_first_appear | @prev_home := ord.home |
+------+------+--------+----------+-----------------+------------------------+
|    9 |   10 | borat  |      700 |               1 |                     10 |
|   10 |   11 | borat  |      700 |               1 |                     11 |
|   12 |   12 | borat  |      700 |               1 |                     12 |
|    8 |   13 | borat  |      700 |               1 |                     13 |
+------+------+--------+----------+-----------------+------------------------+
4 rows in set (0.00 sec)

承認済み回答の送信

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
+------+------+---------------------+--------+----------+
| id   | home | datetime            | player | resource |
+------+------+---------------------+--------+----------+
|    1 |   10 | 2009-04-03 00:00:00 | john   |      399 |
|    2 |   11 | 2009-04-03 00:00:00 | juliet |      244 |
|    5 |   12 | 2009-04-03 00:00:00 | borat  |      555 |
|    8 |   13 | 2009-01-01 00:00:00 | borat  |      700 |
|    9 |   10 | 2009-04-03 00:00:00 | borat  |      700 |
|   10 |   11 | 2009-04-03 00:00:00 | borat  |      700 |
|   12 |   12 | 2009-04-03 00:00:00 | borat  |      700 |
+------+------+---------------------+--------+----------+
7 rows in set (0.00 sec)

また、私はこの答えが大好きです。これは私に非常に役立つので、使用したmysqlシステムに依存するという1つの大きな欠陥を指摘する必要があります。基本的に、このソリューションは副選択のORDER BY句に依存しています。これは、さまざまなmysql環境では機能しない可能性があります。ここで説明し、私は純粋なMySQLの上でそれを試していないが、確かにこれはMariaDB 10.1で確実に動作しません stackoverflow.com/questions/26372511/... が、非常に同じコードがPercona Server上のワークOKを行います。正確には、t1列の量に応じて、同じ結果が得られる場合と得られない場合があります。
Radek、

このステートメントの例は、MariaDB 10.1ではt1テーブルの5つの列を使用したときに機能したことです。6番目の列を追加するとすぐに、元のテーブルの「自然な」データソートをいじくり回して、機能しなくなりました。その理由は、サブセレクトのデータが順序付けられなくなったため、 "is_first_appear = 1"条件が数回満たされたためです。同じデータと同じコードがPerconaで機能しました。
Radek、2016

5

グループごとの各行のランクを基本的に計算し、ランク= 1の場合と同様に最新の行を除外するサブクエリを使用して、グループごとの最新の行をgtする別の方法

select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and a.`datetime` < b.`datetime`
) +1 = 1

デモ

これは、理解を深めるための各行のランクnoの視覚的なデモです

いくつかのコメントを読むことによって、同じ「home」と「datetime」フィールド値を持つ2つの行がある場合はどうでしょうか?

上記のクエリは失敗し、上記の状況では複数の行が返されます。この状況をカバーするには、上記の状況に該当する行を取得するかどうかを決定する別の基準/パラメータ/列が必要になります。サンプルデータセットを表示することによりid、自動インクリメントに設定する必要のある主キー列があると想定します。したがって、この列を使用して、次のCASEようなステートメントを使用して同じクエリを調整することにより、最新の行を選択できます。

select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and  case 
       when a.`datetime` = b.`datetime`
       then a.id < b.id
       else a.`datetime` < b.`datetime`
       end
) + 1 = 1

デモ

上記のクエリは、同じdatetime値の中で最も高いIDを持つ行を選択します

各行のランクなしのビジュアルデモ


2

使用しない理由:SELECT home、MAX(datetime)AS MaxDateTime、player、resource FROM topten GROUP BY home何か見逃しましたか?


4
これはMySQLでのみ有効であり、5.7より前(?)または5.7より後のバージョンでONLY_FULL_GROUP_BYが無効になっている場合にのみ有効になります。 2つの結果フィールド。プレーヤー列はホーム列と相関するため、プレーヤー列では問題になりませんが、リソース列はホーム列または日時列とは相関せず、受け取るリソース値を保証できません。
simpleuser 2015年

説明の+1ですが、この質問ではexpected、MySQLバージョン5.6での出力は返されません。beforeまた、MySQLバージョン5.7およびでそれ以外の動作をすることは間違いありませんafter
sactiw 2015年

@simpleuser、 `プレーヤーの列はホーム列と相関しているため、プレーヤーの列には問題ありません`-詳細を説明できますか?
Istiaque Ahmed 2017

@IstiaqueAhmedをもう一度見てみると、そのステートメントは正しくありません。各プレーヤーは常に同じホームバリューを持っていると思っていましたが、そうではないことがわかりました。そのため、同じ列で同じランダム選択の問題が発生します
simpleuser

1

これを試して

select * from mytable a join
(select home, max(datetime) datetime
from mytable
group by home) b
 on a.home = b.home and a.datetime = b.datetime

よろしく


5
2つの等しい最大日時が同じ家にいる場合(異なるプレーヤーの場合)、それが異なるかどうかをテストします
Maksym Gontar

のエイリアスはmax(datetime) ですdatetime。問題ありませんか?
Istiaque Ahmed 2017

どのように最高のものがdatetime選択されていますか?
Istiaque Ahmed 2017

1

これはあなたが必要とするクエリです:

 SELECT b.id, a.home,b.[datetime],b.player,a.resource FROM
 (SELECT home,MAX(resource) AS resource FROM tbl_1 GROUP BY home) AS a

 LEFT JOIN

 (SELECT id,home,[datetime],player,resource FROM tbl_1) AS b
 ON  a.resource = b.resource WHERE a.home =b.home;

あなたの答えを説明できますか?
Istiaque Ahmed 2017

1

@Michae受け入れられた回答はほとんどの場合に問題なく機能しますが、以下の理由で失敗します。

HomeIDとDatetimeが同じ行が2行ある場合、クエリは、必要に応じて個別のHomeIDではなく、両方の行を返します。そのため、以下のようにDistinct in queryを追加します。

SELECT DISTINCT tt.home  , tt.MaxDateTime
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

結果は次を示します-"#1054-'field list'の不明な列 'tt.MaxDateTime'"
Istiaque Ahmed

@IstiaqueAhmedあなたはMaxDatetimeを提出していますか?つまり、そのような任意の列名..?
Manoj Kargeti

いいえ、OPのテーブルにはそのような列はありません。
Istiaque Ahmed

エラーも同じことを言ってください。正確に何をしたいですか?テーブル構造とクエリを送信できます。
Manoj Kargeti

1

以下のクエリが望ましい出力を与えることを願っています:

Select id, home,datetime,player,resource, row_number() over (Partition by home ORDER by datetime desc) as rownum from tablename where rownum=1
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.