表題のとおり、投票結果を集計したものに順位を付けたいです。
どのようにすべきでしょうか。
調べてみると、遅い早いで複数のアプローチがあるようですが、おすすめの方法をご教示いただければ幸いです。
// 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ページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答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
総合スコア1149
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総合スコア114777
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/09/13 05:55