各グループセットの最大値を含む行をどのように取得しますか?
私はこの質問に過度に複雑なバリエーションを見ましたが、良い答えはありません。私は最も簡単な例をまとめようとしました:
以下のような、個人、グループ、および年齢の列を含む表を考えて、各グループの中で最も年長の人をどのようにして取得しますか。 (グループ内の同点は最初のアルファベット順の結果を与えるべきです)
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
Mysqlでこれを行うための超簡単な方法があります。
select *
from (select * from mytable order by `Group`, age desc, Person) x
group by `Group`
Mysqlでは、notgroup-by-by列を許可することができます。その場合、mysqlはfirstを返すだけです。行。解決策は、まずグループごとに必要な行が最初になるようにデータを順序付けしてから、値を必要な列でグループ化することです。
max()
などを見つけようとする複雑なサブクエリや、同じ最大値を持つ複数の行がある場合に複数の行を返すという問題を回避できます(他の答えと同様)。
注:これはmysql-onlyの解決策です。私が知っている他のすべてのデータベースでは、「集約されていない列はgroup by句にリストされていません」というメッセージでSQL構文エラーが発生します。このソリューションでは記載されていないの動作を使用しているので、remainsworkingと断言するテストを含めることをお勧めします。将来のバージョンのMySQLでこの動作が変更される予定です。
バージョン5.7以降、 sql-mode
の設定にはデフォルトで ONLY_FULL_GROUP_BY
が含まれているので、これを機能させるにはnotにします。 option(この設定を削除するには、サーバーのオプションファイルを編集します)。
正しい解決策は次のとおりです。
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
o
の各行と、b
のすべての行がGroup
列の値が同じで、Age
列の値が大きい行と一致します。列o
にそのグループの最大値を持たないAge
からの行は、b
からの1つ以上の行と一致します。
LEFT JOIN
は、NULL
からのb
sでいっぱいの行( 'グループ内で最大年齢なし')で、グループ内の最も古い人(そのグループ内で一人の人を含む)と一致させます。INNER JOIN
を使用すると、これらの行は一致しなくなり、無視されます
WHERE
文節は、NULL
から抽出されたフィールド内にb
を持つ行だけを保持します。彼らは各グループの最年長の人たちです。
この解決策や他の多くのものは本で説明されています SQLアンチパターン:データベースプログラミングの落とし穴を避ける
MAX(Group)
と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;
SQLite(そしておそらくMySQL)のための私の簡単な解決策:
SELECT *, MAX(age) FROM mytable GROUP BY `Group`;
しかし、PostgreSQLや他のプラットフォームでは動作しません。
PostgreSQLでは DISTINCT ON 句を使用できます。
SELECT DISTINCT ON ("group") * FROM "mytable" ORDER BY "group", "age" DESC;
ランキング方式を使用しています。
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
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;
axiacの解決策は、最終的には私にとって最も効果的でした。しかし、もう1つ複雑さがありました。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;
お役に立てれば!私はこれを行うより良い方法があるはずだと思いますが...
私の解決策はあなたが1つの列だけを検索する必要がある場合にのみうまくいきます、しかし私のニーズのためにパフォーマンスの面で見いだされた最も良い解決策でした(それは1つの単一の質問だけを使います!):
SELECT SUBSTRING_INDEX(GROUP_CONCAT(column_x ORDER BY column_y),',',1) AS xyz,
column_z
FROM table_name
GROUP BY column_z;
それはGROUP_CONCATを使用して順序付けされた連結リストを作成し、それから最初のものだけにサブストリングします。
WHERE IN
を使って簡単な解決策があります
SELECT a.* FROM `mytable` AS a
WHERE a.age IN( SELECT MAX(b.age) AS age FROM `mytable` AS b GROUP BY b.group )
ORDER BY a.group ASC, a.person ASC
CTEの使用 - 一般的な表式:
WITH MyCTE(MaxPKID, SomeColumn1)
AS(
SELECT MAX(a.MyTablePKID) AS MaxPKID, a.SomeColumn1
FROM MyTable1 a
GROUP BY a.SomeColumn1
)
SELECT b.MyTablePKID, b.SomeColumn1, b.SomeColumn2 MAX(b.NumEstado)
FROM MyTable1 b
INNER JOIN MyCTE c ON c.MaxPKID = b.MyTablePKID
GROUP BY b.MyTablePKID, b.SomeColumn1, b.SomeColumn2
--Note: MyTablePKID is the PrimaryKey of MyTable
Wordは予約されているので、Groupを列名として使用しません。しかし、以下のSQLはうまくいきます。
SELECT a.Person, a.Group, a.Age FROM [TABLE_NAME] a
INNER JOIN
(
SELECT `Group`, MAX(Age) AS oldest FROM [TABLE_NAME]
GROUP BY `Group`
) b ON a.Group = b.Group AND a.Age = b.oldest
これが、mysqlで1グループあたりの最大N行数を取得する方法です。
SELECT co.id, co.person, co.country
FROM person co
WHERE (
SELECT COUNT(*)
FROM person ci
WHERE co.country = ci.country AND co.id < ci.id
) < 1
;
使い方:
co.country = ci.country
によって行われます) < 1
によって制御されるので、3要素 - )<3co.id < ci.id
ここに完全な例:
with CTE as
(select Person,
[Group], Age, RN= Row_Number()
over(partition by [Group]
order by Age desc)
from yourtable)`
`select Person, Age from CTE where RN = 1`
MytableからID(およびすべての列)が必要な場合
SELECT
*
FROM
mytable
WHERE
id NOT IN (
SELECT
A.id
FROM
mytable AS A
JOIN mytable AS B ON A. GROUP = B. GROUP
AND A.age < B.age
)
この方法には、別の列でランク付けし、他のデータを破棄しないという利点があります。あなたが商品の列で注文をリストしようとしている状況で非常に役に立ちます。そして、最も重いものを最初にリストします。
出典: 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;
テーブル名を人にする
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;
あなたも試すことができます
SELECT * FROM mytable WHERE age IN (SELECT MAX(age) FROM mytable GROUP BY `Group`) ;