※追記あり。
limit
ついてるし、PostgreSQLですよね?
指針としては、
- row_number() で account_id ごとの連番を作成する(recommend_order)
- このクエリをサブクエリにして、 recommend_order が10以下の行を抽出
SQL
1with query1 as (
2 select * -- ← *は止めて必要な列を列挙すること
3 , row_number() over (pertition by recommend.account_id order by recommend_date, recommend.client_id desc) recommend_order
4 from recommend
5 left join accounts on accounts.id = recommend.account_id
6 left join clients on clients.id = recommend.client_id
7 order by recommend_date, recommend.client_id desc
8)
9select
10 * -- <- こちらも同じく*はやめること。
11from
12 query1
13where recommend_order <= 10 ;
環境が用意できなかったので実際には動かしていません。質問者で文法チェック等して動かしてみてください。
ここから追記。
ほう、MySQL5.7とな。
他の方が大勢指摘している通り、SQLはRDBSにより使える構文や関数がかなり異なりますので製品名を明記しないとこのように頓珍漢な回答が返ってくることになります。以後気を付けるようにね。
で、ウィンドウ関数が使えないMySQL8.0未満は、質問のようなグループごとに集計をかける処理がとっても苦手です。
それでも無理やり書いてみたらこんな感じになるでしょう。
(account_idごと10件まで取得できることは検証しました。
また問題の本質とは関係ないのでaccoutsとclientsのjoinは省略しました)
SQL
1select
2 query.account_id
3 ,query.client_id
4 ,query.recommend_date
5 ,query.row_number
6from(
7 select
8 main_r.account_id
9 ,main_r.client_id
10 ,main_r.recommend_date
11 ,(select
12 count(*)
13 from
14 recommend sub_r1
15 where sub_r1.account_id = main_r.account_id and (
16 (sub_r1.recommend_date < main_r.recommend_date)
17 or (sub_r1.recommend_date = main_r.recommend_date and sub_r1.client_id > main_r.client_id))
18 ) +1 row_number
19 from
20 recommend main_r
21 order by row_number
22) query
23where query.row_number <= 10
24order by account_id,row_number
25;
count(*)があるサブクエリでrow_number相当の処理をやっています。比較条件を1つずつバラして書くのが大変です。
今回の件ではまだ条件が単純なのでcountとwhere句でやっつけられていますが、もっと複雑な集計条件になると黒魔術なSQLが爆誕することでしょう。
グループから上位N件を取得するクエリの作成は、皆さん本当に苦労されていて、
teratailの過去質問でも例えばこれとか、はたまたこんなのとかいろいろあるのですが、どの質問も「クエリがエレガントじゃねぇなあ」とぼやきながら回答されてます。
本件も私の正直なところ、クエリをフェッチした先で1行ずつ番号付けながら読み込み、同じaccount_id の 11件目以降は読み飛ばすみたいな処理をしたほうが精神衛生上遥かにマシだと考えています。(多分PHPとかrailsとか使っていますよね)
今回の問題は上にかいたもので解決できますが、実際に作るクエリはこれ1つではないと思いますし、取得先で集計作業したほうがきっと楽ですよ、とお伝えしておきます。
以下検証用。
CREATE TABLE `recommend` (
`account_id` int(11) DEFAULT NULL,
`client_id` int(11) DEFAULT NULL,
`recommend_date` varchar(8) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into db.recommend values (1,1,'20180101');
insert into db.recommend values (1,1,'20180201');
insert into db.recommend values (1,1,'20180301');
insert into db.recommend values (1,2,'20180101');
insert into db.recommend values (1,2,'20180201');
insert into db.recommend values (1,2,'20180301');
insert into db.recommend values (1,3,'20180101');
insert into db.recommend values (1,3,'20180201');
insert into db.recommend values (1,3,'20180301');
insert into db.recommend values (2,1,'20180102');
insert into db.recommend values (2,1,'20180202');
insert into db.recommend values (2,1,'20180302');
insert into db.recommend values (2,2,'20180102');
insert into db.recommend values (2,2,'20180202');
insert into db.recommend values (2,2,'20180302');
insert into db.recommend values (2,3,'20180102');
insert into db.recommend values (2,3,'20180202');
insert into db.recommend values (2,3,'20180302');
insert into db.recommend values (1,11,'20180101');
insert into db.recommend values (1,11,'20180201');
insert into db.recommend values (1,11,'20180301');
insert into db.recommend values (1,12,'20180101');
insert into db.recommend values (1,12,'20180201');
insert into db.recommend values (1,12,'20180301');
insert into db.recommend values (1,13,'20180101');
insert into db.recommend values (1,13,'20180201');
insert into db.recommend values (1,13,'20180301');
insert into db.recommend values (2,11,'20180102');
insert into db.recommend values (2,11,'20180202');
insert into db.recommend values (2,11,'20180302');
insert into db.recommend values (2,12,'20180102');
insert into db.recommend values (2,12,'20180202');
insert into db.recommend values (2,12,'20180302');
insert into db.recommend values (2,13,'20180102');
insert into db.recommend values (2,13,'20180202');
insert into db.recommend values (2,13,'20180302');