300

グループ化された各セットの最大値を含む行を取得するにはどうすればよいですか?

この質問については、非常に複雑なバリエーションがいくつか見られましたが、適切な答えはありませんでした。最も単純な例をまとめてみました。

以下のような表に、人物、グループ、年齢の列がある場合、各グループの最年長者を取得するにはどうすればよいでしょうか? (グループ内で同点の場合は、アルファベット順で最初の結果を与える必要があります)

Person | Group | Age
---
Bob  | 1     | 32  
Jill | 1     | 34  
Shawn| 1     | 42  
Jake | 2     | 29  
Paul | 2     | 36  
Laura| 2     | 39  

望ましい結果セット:

Shawn | 1     | 42    
Laura | 2     | 39  
4

19 に答える 19

373

正しい解決策は次のとおりです。

SELECT o.*
FROM `Persons` o                    # 'o' from 'oldest person in group'
  LEFT JOIN `Persons` b             # 'b' from 'bigger age'
      ON o.Group = b.Group AND o.Age < b.Age
WHERE b.Age is NULL                 # bigger age not found

使い方:

の各行を、 column に同じ値を持ち、 columnに大きな値を持つoすべての行と一致させます。列内のグループの最大値を持たないからの行は、 からの1 つまたは複数の行と一致します。bGroupAgeoAgeb

は、グループ内の最年長の人物 (グループ内で 1 人でいる人物を含む) と、 from (「グループ内で最大の年齢はありません」)LEFT JOINでいっぱいの行に一致させます。を使用すると、これらの行が一致しなくなり、無視されます。NULLb
INNER JOIN

このWHERE句は、NULLから抽出されたフィールドに がある行のみを保持しbます。彼らは各グループの最年長者です。

さらなる読み物

この解決策と他の多くの解決策は、本SQL アンチパターン: データベース プログラミングの落とし穴の回避で説明されています。

于 2015-01-22T13:56:25.663 に答える
141

mysql でこれを行うための非常に簡単な方法があります。

select * 
from (select * from mytable order by `Group`, age desc, Person) x
group by `Group`

これが機能するのは、mysqlではグループ化されていない列を集計できないためです。この場合、mysql は最初の行を返すだけです。解決策は、最初に各グループで必要な行が最初になるようにデータを並べ替え、次に値が必要な列でグループ化することです。

などを見つけようとする複雑なサブクエリmax()や、同じ最大値を持つ行が複数ある場合に複数の行を返す問題も回避します (他の回答と同様)。

注:これはmysql のみのソリューションです。私が知っている他のすべてのデータベースは、「集計されていない列は句によってグループにリストされていません」などのメッセージで SQL 構文エラーをスローします。このソリューションは文書化されていない動作を使用するため、より慎重な方、MySQL の将来のバージョンでこの動作が変更された場合でも機能し続けることを確認するテストを含めることをお勧めします。

バージョン 5.7 の更新:

バージョン 5.7 以降、sql-mode設定にはONLY_FULL_GROUP_BYデフォルトで含まれているため、これを機能させるには、このオプションを使用しないでください(サーバーのオプション ファイルを編集して、この設定を削除します)。

于 2012-08-24T01:55:02.393 に答える
75

MAX(Group)andをプルするサブクエリに対して結合できますAge。このメソッドは、ほとんどの RDBMS で移植可能です。

SELECT t1.*
FROM yourTable t1
INNER JOIN
(
    SELECT `Group`, MAX(Age) AS max_age
    FROM yourTable
    GROUP BY `Group`
) t2
    ON t1.`Group` = t2.`Group` AND t1.Age = t2.max_age;
于 2012-08-24T01:39:31.763 に答える
6

MySQL に row_number 関数があるかどうかは不明です。その場合は、それを使用して目的の結果を得ることができます。SQL Server では、次のようなことができます。

CREATE TABLE p
(
 person NVARCHAR(10),
 gp INT,
 age INT
);
GO
INSERT  INTO p
VALUES  ('Bob', 1, 32);
INSERT  INTO p
VALUES  ('Jill', 1, 34);
INSERT  INTO p
VALUES  ('Shawn', 1, 42);
INSERT  INTO p
VALUES  ('Jake', 2, 29);
INSERT  INTO p
VALUES  ('Paul', 2, 36);
INSERT  INTO p
VALUES  ('Laura', 2, 39);
GO

SELECT  t.person, t.gp, t.age
FROM    (
         SELECT *,
                ROW_NUMBER() OVER (PARTITION BY gp ORDER BY age DESC) row
         FROM   p
        ) t
WHERE   t.row = 1;
于 2015-12-10T21:56:46.697 に答える
4

ランキング方式を採用。

SELECT @rn :=  CASE WHEN @prev_grp <> groupa THEN 1 ELSE @rn+1 END AS rn,  
   @prev_grp :=groupa,
   person,age,groupa  
FROM   users,(SELECT @rn := 0) r        
HAVING rn=1
ORDER  BY groupa,age DESC,person

この sql は次のように説明できます。

  1. select * from users, (select @rn := 0) r groupa、age desc、person による順序

  2. @prev_grp はヌルです

  3. @rn := CASE WHEN @prev_grp <> groupa THEN 1 ELSE @rn+1 END

    これは次のような 3 つの演算子式
    です。 rn = 1 if prev_grp != groupa else rn=rn+1

  4. rn=1 を使用すると、必要な行が除外されます

于 2012-08-24T01:46:23.093 に答える
2

axiacのソリューションは、最終的に私にとって最も効果的でした。ただし、さらに複雑になりました。2 つの列から派生した計算された「最大値」です。

同じ例を使用してみましょう: 各グループの最年長の人がいいと思います。同じ年齢の人がいる場合は、最も背の高い人を選びます。

この動作を得るには、左結合を 2 回実行する必要がありました。

SELECT o1.* WHERE
    (SELECT o.*
    FROM `Persons` o
    LEFT JOIN `Persons` b
    ON o.Group = b.Group AND o.Age < b.Age
    WHERE b.Age is NULL) o1
LEFT JOIN
    (SELECT o.*
    FROM `Persons` o
    LEFT JOIN `Persons` b
    ON o.Group = b.Group AND o.Age < b.Age
    WHERE b.Age is NULL) o2
ON o1.Group = o2.Group AND o1.Height < o2.Height 
WHERE o2.Height is NULL;

お役に立てれば!これを行うにはもっと良い方法があるはずだと思いますが...

于 2016-09-14T13:30:45.620 に答える
1

あなたも試すことができます

SELECT * FROM mytable WHERE age IN (SELECT MAX(age) FROM mytable GROUP BY `Group`) ;
于 2014-10-25T19:00:55.217 に答える
0

テーブル名を人にする

select O.*              -- > O for oldest table
from people O , people T
where O.grp = T.grp and 
O.Age = 
(select max(T.age) from people T where O.grp = T.grp
  group by T.grp)
group by O.grp; 
于 2016-07-10T11:31:26.590 に答える
0

この方法には、別の列でランク付けできるという利点があり、他のデータを破棄する必要はありません。アイテムの列を使用して注文をリストし、最も重いものを最初にリストする場合に非常に便利です。

ソース: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat

SELECT person, group,
    GROUP_CONCAT(
        DISTINCT age
        ORDER BY age DESC SEPARATOR ', follow up: '
    )
FROM sql_table
GROUP BY group;
于 2015-03-13T14:30:12.397 に答える