MySQLは以下のような明らかに間違ったGroupByのSQLを
実行してもエラーになりません。
(結構びっくりな仕様です)
select MEMBER_STATUS_CODE
, MEMBER_NAME
from MEMBER
group by MEMBER_STATUS_CODE;
で、それはちょっと勘弁ということで、できればエラーになるように
したいので、以下のようにmy.ini(or my.cnf)のSQL-Modeを設定します。
sql_mode="ONLY_FULL_GROUP_BY"
こうすると、さっきのSQLがちゃんとエラーになります。
DBFluteのドキュメントの「MySQLの扱い」に書かれていることです。
http://dbflute.sandbox.seasar.org/contents/dbvendor/mysql.html
(「SQLモードのススメ」をご覧下さい)
で、この話はMySQL-5.0.45では全く問題なく動作していのたですが、
MySQL-5.1.30で同じ環境で試してみたところ、以下のSQLが落ちるように
なってしまいました。
select max(LOGIN_DATETIME)
from MEMBER_LOGIN
where LOGIN_MOBILE_FLG = 0
これはさっきのSQLとは全然関係のないSQLです。
GroupByは利用しないで単にmax()を使っています。
普通に全く問題のないSQLで、MySQL-5.0.45では正常に
動作していました。他のDBでも同じSQLが正常に動作します。
このSQLが以下のようなエラーを出すようになってしまいました。
ERROR 1140 (42000):
Mixing of GROUP columns (MIN(),MAX(),COUNT(),...)
with no GROUP columns is illegal
if there is no GROUP BY clause
うーむー、おかしい。。。と思い、「ONLY_FULL_GROUP_BY」を
除去してもう一度試してみました。すると、正常に動作しました。
[MySQL-5.1.30]
ONLY_FULL_GROUP_BYあり : NG
ONLY_FULL_GROUP_BYなし : OK
これって。。。
推測でしかないのですが、
ONLY_FULL_GROUP_BYの制約処理が誤作動して、
問題ないSQLもエラーにしてしまっている!?
ではないかと考えてしまいます。
実際どうなのかわからないのですが、なんにせよ困ります。
DBFluteのMySQLを使ったExample「dbflute-mysql-example」
は、もともとこの「ONLY_FULL_GROUP_BY」を付与していたのですが、
現在は除去しています。Exampleは基本5.0.45を利用していますが、
切り替えて試すときなどに面倒なので。
しかし、こうするとさっきの間違ったGroupByが通ってしまいます。
「dbflute-mysql-example」
https://www.seasar.org/svn/sandbox/dbflute/trunk/dbflute-mysql-example
実は、DBFluteの次バージョン「0.8.8」にて、
ScalarSubQueryなる機能を実装しているのですが、
上記の問題に見事に引っ掛かってしまいました。
もし、MySQL-5.1.xでこの機能を利用される方は、SQL-Modeの
環境設定に関しては注意するようにして下さい。
★追記:
「5.0.67-community-nt」でも発生するようです。
5.1からってわけじゃなさそうです。
MySQL-5.0.45は発生しない、MySQL-5.0.67は発生する。
★追記:
http://bugs.mysql.com/bug.php?id=39656
にてバグとして登録されているようです。
★追記:
5.0.75で修正されているようですが、
http://www.mysql.gr.jp/frame/modules/news/article.php?storyid=125
ソースコードだけのリリースだそうです。
★追記:
5.0.77でバイナリリリースされています。
一件落着...
ScalarConditionは以下のような感じ:
MemberCB cb = new MemberCB();
cb.query().setMemberStatusCode_Equal_Formalized();
cb.query().scalar_Equal().max(new SubQuery<MemberCB>() {
public void query(MemberCB subCB) {
subCB.specify().columnMemberBirthday();
subCB.query().setMemberStatusCode_Equal_Formalized();
}
});
で、このSQLがまさしくさっき紹介した最初のSQLと同じ構造を含みます。
業務で非常に良く使うSQLであり、まさしく「定型的なSQL」ということで、
ConditionBeanの機能として実装しました。
https://www.seasar.org/issues/browse/DBFLUTE-379
select dflocal.MEMBER_NAME as MEMBER_NAME, ...
from MEMBER dflocal
where dflocal.MEMBER_STATUS_CODE = 'FML'
and dflocal.MEMBER_BIRTHDAY = (
select max(dfsublocal_0.MEMBER_BIRTHDAY)
from MEMBER dfsublocal_0
where dfsublocal_0.MEMBER_STATUS_CODE = 'FML'
)