質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

Q&A

解決済

2回答

7253閲覧

SQLのCOUNTカラムに順位をつけるには

supermaruetsu

総合スコア148

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

0グッド

1クリップ

投稿2017/08/30 10:12

表題のとおり、投票結果を集計したものに順位を付けたいです。
どのようにすべきでしょうか。
調べてみると、遅い早いで複数のアプローチがあるようですが、おすすめの方法をご教示いただければ幸いです。

// vote table id good --------- 1 4 2 4 3 7 4 1 5 6 6 1 7 4 8 8 9 8

下記のSQL実行

php

1 return DB::table('vote') 2 ->select('good', DB::raw('count(good) as COUNT')) 3 ->groupBy('good') 4 ->orderBy('COUNT', 'desc') 5 ->get();

結果

good COUNT 4 3 1 2 8 2 7 1 6 1

COUNTの横にRANKカラムを追加して、かつ同順位を表示できればBestです。

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

MySQL 5.7までは分析関数が使えないので、この手の処理はかなり面倒です。

まずはこちらのユーザ変数を使った方法を参考にしてみました。

Dense Rank function In Mysql SQL Query (Create / Implement)

sql

1SET @priv = NULL ; 2SET @nbr = 0 ; 3SET @rank = 0 ; 4 5SELECT 6 good 7 ,`COUNT` 8 ,@nbr := @nbr + 1 AS nbr 9 ,@rank := IF(`COUNT` = @priv, @rank, @nbr) AS rank 10 ,@priv := `COUNT` 11FROM ( 12 SELECT 13 good 14 ,COUNT(good) AS `COUNT` 15 FROM 16 vote 17 GROUP BY 18 good 19 ORDER BY 20 `COUNT` DESC 21) AS tmp 22;

これでとりあえず結果は得られていますが、どうやらMySQLではSELECT句にユーザ変数を指定した順序とそれらの評価順序が同じであることを保証しないとのことです。(日本語訳がおかしいので英語版から)

MySQL 5.7 9.4 User-Defined Variables

For other statements, such as SELECT, you might get the results you expect, but this is not guaranteed. In the following statement, you might think that MySQL will evaluate @a first and then do an assignment second:

SELECT @a, @a:=@a+1, ...;

However, the order of evaluation for expressions involving user variables is undefined.

つまり、以下の部分が意図した結果になるかどうか保証されないようです。

SQL

1 ,@nbr := @nbr + 1 AS nbr 2 ,@rank := IF(`COUNT` = @priv, @rank, @nbr) AS rank 3 ,@priv := `COUNT`

ユーザ変数の評価順に依存しない書き方や、ユーザ変数を使わない書き方を考えてみましたが、いずれも複雑です。

sql

1-- 集計結果を自身と1行ずらして結合 2-- COUNTに変化がない場合ひとつ前のRANKを用いる 3 4SET @nbr1 = 0 ; 5SET @nbr2 = 0 ; 6SET @rnk = 1 ; 7 8SELECT 9 tmp1.good 10 ,tmp1.`COUNT` 11-- ,tmp2.`COUNT` 12 ,CASE WHEN tmp1.`COUNT` <> tmp2.`COUNT` 13 THEN @rnk := tmp1.nbr 14 ELSE @rnk 15 END AS `RANK` 16FROM ( 17 SELECT 18 good ,`COUNT` 19 ,@nbr1 := @nbr1 + 1 AS nbr 20 FROM ( 21 SELECT good ,COUNT(good) AS `COUNT` 22 FROM vote 23 GROUP BY good 24 ORDER BY `COUNT` DESC 25 ) AS tmpA 26) AS tmp1 27LEFT OUTER JOIN ( 28 SELECT 29 good ,`COUNT` 30 ,@nbr2 := @nbr2 + 1 AS nbr 31 FROM ( 32 SELECT good ,COUNT(good) AS `COUNT` 33 FROM vote 34 GROUP BY good 35 ORDER BY `COUNT` DESC 36 ) AS tmpB 37) AS tmp2 38 ON tmp1.nbr - 1 = tmp2.nbr 39ORDER BY 40 tmp1.nbr 41;

sql

1-- COUNTの値を集計結果内の他のレコードと比較して 2-- RANKを求める 3 4SELECT 5 good 6 ,`COUNT` 7 ,( 8 SELECT COUNT(*) + 1 9 FROM ( 10 SELECT good ,COUNT(good) AS `COUNT` 11 FROM vote 12 GROUP BY good 13 ) AS tmp2 14 WHERE tmp1.`COUNT` < tmp2.`COUNT` 15 ) AS `RANK` 16FROM ( 17 SELECT good ,COUNT(good) AS `COUNT` 18 FROM vote 19 GROUP BY good 20) AS tmp1 21ORDER BY 22 `COUNT` DESC 23;

PHP側でランクを振った方がシンプルでしょうし、PHPでやった方がサブクエリが必要ない分速度についても優位かと思います。

投稿2017/08/30 16:57

SVC34

総合スコア1149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

supermaruetsu

2017/09/13 05:55

PHPのフレームワーク内でのユーザ変数の使い方が分からず、またご指摘いただいた速度面でのデメリットもあった為PHP側でランクを振る手法にしました。すごく丁寧にアドバイス頂き大大大感謝です。ありがとうございました。
guest

0

適切なインデックスが貼ってあるならご提示のgroup byの集計がベストです。
ランクは集計後のCOUNTで行うのでしょうか?
同じCOUNT数の場合同順とし、その後のデータが後ろたおしすればいいのでしょうか?
例示のものでいうとcount=3が1位、count=2が2位、count=1が4位?

追記

集計後のデータを最終形すると処理自体は冗長になります
とりあえずこうしてみてください

SQL

1select good,count, 2(select count(*)+1 from 3(select good,count(*) as count from vote group by good) as t2 4where t1.count<t2.count) as rank 5from (select good,count(*) as count from vote group by good) as t1 6order by rank asc,good asc

投稿2017/08/30 11:22

編集2017/08/31 00:32
yambejp

総合スコア114777

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

supermaruetsu

2017/08/30 11:31

はい、おっしゃる通りCOUNTの数が多い順に1位2位...と付けたいです。3位もおっしゃる通り存在せず、4位がきます。初心者のため、お恥ずかしながらインデックスという言葉にもピンと来ません。。。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問