ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

MySQL では GROUP BY 句を使用した場合でも集約されていない列を結果に含めることができる

表題どおり。

普通、SQL で GROUP BY 句を使用して SELECT する場合、結果に含めることができる列は GROUP BY 句に含まれる列か集約関数 (aggregate functions) を適用した列のみです。 しかし、MySQL の場合はそうではなく、GROUP BY 句にも含まれていないし集約関数も通していない列を結果に含めることができます。

仕事で触ってるプログラムの中にそういうクエリを発行する SQL があって 「マジか!!」 って思ったのですが、調べてみるとパフォーマンスのためにそういう拡張がなされているとのことでした。

MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. This means that the preceding query is legal in MySQL. You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Sorting of the result set occurs after values have been chosen, and ORDER BY does not affect which values the server chooses.

http://dev.mysql.com/doc/refman/5.6/en/group-by-hidden-columns.html

CREATE TABLE example_table ( a INT, b INT, c INT );

上のように作ったテーブルが存在する場合、列 a でグループ化してそれぞれの a の値に対する b の値の最大値を求めるクエリは下のようになります。 この SQLPostgreSQL でも MySQL でも普通に結果を返してくれます。

SELECT a, MAX(b) FROM example_table GROUP BY a;

さらに、結果に列 c を含めようとすると、下のようなクエリになります。

SELECT a, MAX(b), c FROM example_table GROUP BY a;

PostgreSQL でこのクエリを投げると、列 c が GROUP BY にも含まれないし集約関数を通しているわけでもないので以下のようにエラーになります。 (PostgreSQL 9.1 で確認。)

ERROR:  column "example_table.c" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT a, MAX(b), c FROM example_table GROUP BY a;
                          ^

一方で MySQL の場合はエラーにならずに結果を返してくれます。 (MySQL 5.5 で確認。) MySQL サーバーは、集約されていない列の値に関しては各グループの中から自由に選ぶ (The server is free to choose any value from each group) とのことです。

ONLY_FULL_GROUP_BY モード (追記)

集約関数を通していない値が結果に含まれるような SQL を拒否させるための ONLY_FULL_GROUP_BY という SQL MODE があります。 5.7.5 以降ではデフォルトで有効になっているみたいですね。