mazeltov7のweb断片

備忘録的なテキトーなことを書きます。(技術記事はQiitaに移行しました http://qiita.com/mazeltov7 )

Mysql5.6から5.7にしたらONLY_FULL_GROUP_BYがオンになってエラーなったので、なおした

これはなに

Mysql5.7でONLY_FULL_GROUP_BYがデフォルトでオンになってて、それに関連してGroup byでテキトーにやってたところにエラーが出た。
以下、エラー出た箇所と対処を書く。
(ちなみに、上記設定をdisableにすることもできるけどnot recommend)

なにやったか

まず元々どうなってたか書く。 たとえば、簡単にtableを、

+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| movieid   | varchar(255) | YES  | UNI | NULL    |                |
| userid    | varchar(255) | YES  |     | NULL    |                |
| lang      | varchar(255) | YES  |     | NULL    |                |
| country   | varchar(255) | YES  |     | NULL    |                |
| image     | varchar(255) | YES  |     | NULL    |                |
| duration  | int(11)      | YES  |     | NULL    |                |
| created   | int(11)      | YES  |     | NULL    |                |
| maxview   | int(11)      | YES  |     | NULL    |                |
| totalview | int(11)      | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+

でここから、

mysql > select * from table_name where country = 'us' and duration >= 2000 
             and created >= OO and created <= OO
             group by userid having count(userid) >= 1 ;

とする。で、たとえば、having以下の条件を変えて、countryがusでdurationが2000以上の配信のうちcreatedがOOからOOまでの期間で、1回以上配信してる人、3回以上配信してる人みたいなのを出そうとしてた。
で、これで条件にあるデータが返ってきてたけど、よく考えると、同じユーザーでもmovieidは違うので、ちょっときもいけど、勝手にidが一番小さいやつを選んで出力してた。

ONLY_FULL_GROUP_BYだと怒られる。

ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db_name.table_name.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

Roland Bouman's blog: MySQL 5.7.5: GROUP BY respects functional dependencies!

MySQL :: MySQL 5.7 Reference Manual :: 12.20.3 MySQL Handling of GROUP BY

てことで、明示的に指定する。さらに上記のやり方ではidが小さい特定のデータを取ってたけど、そのユーザーのavg情報を出すことにする。

mysql > select userid, lang, country, image, avg(duration), avg(maxview), avg(totalview) 
              from table_name where country = 'us' and duration >= 2000
              and created >= OO and created <= OO
              group by userid, lang, country, image having count(userid) >= 1 ;

一意になるcolumnをきちんとgroup byに明示的に書いてあげる。selectにもしかり。で、すっきりした出力結果になる。