以下は可能な限り最も単純な例ですが、どのソリューションでも、上位n個の結果が必要な場合にスケーリングできる必要があります。
以下のようなテーブルに人、グループ、年齢の列がある場合、各グループの最も古い2人をどのように取得しますか?(グループ内の関係これ以上の結果は得られませんが、最初の2つをアルファベット順に指定してください)
+ -------- + ------- + ----- + |人|グループ|年齢| + -------- + ------- + ----- + |ボブ| 1 | 32 | |ジル| 1 | 34 | |ショーン| 1 | 42 | |ジェイク| 2 | 29 | |ポール| 2 | 36 | |ローラ| 2 | 39 | + -------- + ------- + ----- +
望ましい結果セット:
+ -------- + ------- + ----- + |ショーン| 1 | 42 | |ジル| 1 | 34 | |ローラ| 2 | 39 | |ポール| 2 | 36 | + -------- + ------- + ----- +
注:この質問は以前のものに基づいています- グループ化されたSQL結果の各グループの最大値でレコードを取得 -取得のため各グループの1行で、@ BohemianからMySQL固有の優れた回答を受け取りました。
select *
from (select * from mytable order by `Group`, Age desc, Person) x
group by `Group`
方法はわかりませんが、これを構築できることを楽しみにしています。
これを行う1つの方法は、UNION ALL
を使用することです( SQL Fiddle with Demo を参照)。これは2つのグループで機能します。3つ以上のグループがある場合は、group
番号を指定し、各group
にクエリを追加する必要があります。
(
select *
from mytable
where `group` = 1
order by age desc
LIMIT 2
)
UNION ALL
(
select *
from mytable
where `group` = 2
order by age desc
LIMIT 2
)
これを行うにはさまざまな方法があります。この記事を参照して、状況に最適なルートを決定してください。
http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
編集:
これもあなたのために働くかもしれません、それは各レコードの行番号を生成します。上記のリンクの例を使用すると、行番号が2以下のレコードのみが返されます。
select person, `group`, age
from
(
select person, `group`, age,
(@num:=if(@group = `group`, @num +1, if(@group := `group`, 1, 1))) row_number
from test t
CROSS JOIN (select @num:=0, @group:=null) c
order by `Group`, Age desc, person
) as x
where x.row_number <= 2;
デモ を参照してください
他のデータベースでは、ROW_NUMBER
を使用してこれを行うことができます。 MySQLはROW_NUMBER
をサポートしませんが、変数を使用してエミュレートできます:
SELECT
person,
groupname,
age
FROM
(
SELECT
person,
groupname,
age,
@rn := IF(@prev = groupname, @rn + 1, 1) AS rn,
@prev := groupname
FROM mytable
JOIN (SELECT @prev := NULL, @rn := 0) AS vars
ORDER BY groupname, age DESC, person
) AS T1
WHERE rn <= 2
オンラインで動作することを確認してください: sqlfiddle
Editbluefeetが非常によく似た答えを投稿していることに気付いた:彼に+1。ただし、この回答には2つの小さな利点があります。
だから、誰かを助けることができるなら、ここに置いておきます.
これを試して:
SELECT a.person, a.group, a.age FROM person AS a WHERE
(SELECT COUNT(*) FROM person AS b
WHERE b.group = a.group AND b.age >= a.age) <= 2
ORDER BY a.group ASC, a.age DESC
自己結合の使用はどうですか:
CREATE TABLE mytable (person, groupname, age);
INSERT INTO mytable VALUES('Bob',1,32);
INSERT INTO mytable VALUES('Jill',1,34);
INSERT INTO mytable VALUES('Shawn',1,42);
INSERT INTO mytable VALUES('Jake',2,29);
INSERT INTO mytable VALUES('Paul',2,36);
INSERT INTO mytable VALUES('Laura',2,39);
SELECT a.* FROM mytable AS a
LEFT JOIN mytable AS a2
ON a.groupname = a2.groupname AND a.age <= a2.age
GROUP BY a.person
HAVING COUNT(*) <= 2
ORDER BY a.groupname, a.age DESC;
私に与えます:
a.person a.groupname a.age
---------- ----------- ----------
Shawn 1 42
Jill 1 34
Laura 2 39
Paul 2 36
各カテゴリのトップ10レコードを選択 に対するBill Karwinの回答に強く触発されました
また、私はSQLiteを使用していますが、これはMySQLで動作するはずです。
別のこと:上記では、便宜上、group
列をgroupname
列に置き換えました。
編集:
タイの結果が欠落していることに関するOPのコメントに続いて、スナフィンの回答を増やして、すべてのタイを表示しました。つまり、最後の行が同数の場合、以下に示すように、3行以上を返すことができます。
.headers on
.mode column
CREATE TABLE foo (person, groupname, age);
INSERT INTO foo VALUES('Paul',2,36);
INSERT INTO foo VALUES('Laura',2,39);
INSERT INTO foo VALUES('Joe',2,36);
INSERT INTO foo VALUES('Bob',1,32);
INSERT INTO foo VALUES('Jill',1,34);
INSERT INTO foo VALUES('Shawn',1,42);
INSERT INTO foo VALUES('Jake',2,29);
INSERT INTO foo VALUES('James',2,15);
INSERT INTO foo VALUES('Fred',1,12);
INSERT INTO foo VALUES('Chuck',3,112);
SELECT a.person, a.groupname, a.age
FROM foo AS a
WHERE a.age >= (SELECT MIN(b.age)
FROM foo AS b
WHERE (SELECT COUNT(*)
FROM foo AS c
WHERE c.groupname = b.groupname AND c.age >= b.age) <= 2
GROUP BY b.groupname)
ORDER BY a.groupname ASC, a.age DESC;
私に与えます:
person groupname age
---------- ---------- ----------
Shawn 1 42
Jill 1 34
Laura 2 39
Paul 2 36
Joe 2 36
Chuck 3 112
これをチェックしてください:
SELECT
p.Person,
p.`Group`,
p.Age
FROM
people p
INNER JOIN
(
SELECT MAX(Age) AS Age, `Group` FROM people GROUP BY `Group`
UNION
SELECT MAX(p3.Age) AS Age, p3.`Group` FROM people p3 INNER JOIN (SELECT MAX(Age) AS Age, `Group` FROM people GROUP BY `Group`) p4 ON p3.Age < p4.Age AND p3.`Group` = p4.`Group` GROUP BY `Group`
) p2 ON p.Age = p2.Age AND p.`Group` = p2.`Group`
ORDER BY
`Group`,
Age DESC,
Person;
SQLフィドル: http://sqlfiddle.com/#!2/cdbb6/15
Snuffinソリューションは、行がたくさんあり、Mark Byers/Rick JamesおよびBluefeetソリューションが私の環境(MySQL 5.6)で動作しない場合、実行が非常に遅いようです。selectbyの実行後にorder byが適用されるため、ここにバリアントがありますこの問題を解決するためのマーク・バイヤーズ/リック・ジェームスのソリューション(追加のインブリケートされた選択):
select person, groupname, age
from
(
select person, groupname, age,
(@rn:=if(@prev = groupname, @rn +1, 1)) as rownumb,
@prev:= groupname
from
(
select person, groupname, age
from persons
order by groupname , age desc, person
) as sortedlist
JOIN (select @prev:=NULL, @rn :=0) as vars
) as groupedlist
where rownumb<=2
order by groupname , age desc, person;
500万行のテーブルで同様のクエリを実行したところ、3秒未満で結果が返されました
他の回答が十分に速くない場合 このコード を試してください:
SELECT
province, n, city, population
FROM
( SELECT @prev := '', @n := 0 ) init
JOIN
( SELECT @n := if(province != @prev, 1, @n + 1) AS n,
@prev := province,
province, city, population
FROM Canada
ORDER BY
province ASC,
population DESC
) x
WHERE n <= 3
ORDER BY province, n;
出力:
+---------------------------+------+------------------+------------+
| province | n | city | population |
+---------------------------+------+------------------+------------+
| Alberta | 1 | Calgary | 968475 |
| Alberta | 2 | Edmonton | 822319 |
| Alberta | 3 | Red Deer | 73595 |
| British Columbia | 1 | Vancouver | 1837970 |
| British Columbia | 2 | Victoria | 289625 |
| British Columbia | 3 | Abbotsford | 151685 |
| Manitoba | 1 | ...
これを共有したかったのは、作業中のJavaプログラムでこれを簡単に実装する方法を探すのに長い時間を費やしたからです。これは、探している出力を提供するものではなく、近いものです。 GROUP_CONCAT()
というmysqlの関数は、各グループで返す結果の数を指定するのに非常にうまく機能しました。 LIMIT
またはCOUNT
を使用してこれを実行しようとする他の凝った方法を使用することは、私にとってはうまくいきませんでした。そのため、変更された出力を受け入れたい場合、それは素晴らしい解決策です。学生ID、性別、GPAを含む「student」というテーブルがあるとします。性別ごとに5 gpaを超えたいとしましょう。次に、このようなクエリを書くことができます
SELECT sex, SUBSTRING_INDEX(GROUP_CONCAT(cast(gpa AS char ) ORDER BY gpa desc), ',',5)
AS subcategories FROM student GROUP BY sex;
パラメータ「5」は、各行に連結するエントリの数を示します。
そして、出力は次のようになります
+--------+----------------+
| Male | 4,4,4,4,3.9 |
| Female | 4,4,3.9,3.9,3.8|
+--------+----------------+
ORDER BY
変数を変更して、別の方法で並べ替えることもできます。したがって、学生の年齢があった場合、「gpa desc」を「age desc」に置き換えることができます。 group byステートメントに変数を追加して、出力の列を増やすこともできます。したがって、これは非常に柔軟で、結果を一覧表示するだけで問題ない場合に適切に機能することがわかりました。
SQL Serverのrow_numer()
は、以下のように簡単に結果を取得できる強力な関数です
select Person,[group],age
from
(
select * ,row_number() over(partition by [group] order by age desc) rn
from mytable
) t
where rn <= 2