MySQLの補足資料

概要

DBFluteとは直接は無関係ながらも関わりの深い、MySQLに関する補足資料です。 厳密な仕様はDBMSのオフィシャルドキュメントの通りではありますが、ここでは簡易な資料としてまとめています。

SQL周り

全文検索

MyISAM もしくは "Tritonn + Senna" において全文検索ができます。

e.g. foo という文字列が含まれる会員名称を全文検索 @SQL
match(MEMBER_NAME) against ('foo' in boolean mode)

DBFluteでは、この構文が ConditionBean で利用できます。

更新時のサブクエリでの制限

update 文の where 句のサブクエリにて更新対象のテーブルを参照できないという制限があります。 また、delete 文も同じです。DBFlute の queryUpdate(), queryDelete() では、その制限により ConditionBean の条件で指定できない機能が存在します。

SQL関数

現在日付

current_date()
yyyy-MM-dd
current_time()
HH:mm:ss
current_timestamp()
yyyy-MM-dd HH:mm:ss
now()
current_timestamp() と同じ

null の場合のデフォルト値

coalesce() 関数を利用することで、対象値が null の場合のデフォルト値を設定することができます。

e.g. 購入数量の平均値が null の場合は 0 にする @SQL
coalesce(avg(PURCHASE_COUNT), 0)
  • デフォルト値の部分はバインド変数が適用できます

DBFluteでは、ConditionBeanのSQL関数フィルタの coalesce() にてこれを利用しています。

数値の丸め

round() 関数を利用することで数値の丸めができます。厳密な丸め仕様はMySQLの仕様に依存します。

e.g. 購入数量の平均値の小数点を丸める @SQL
round(avg(PURCHASE_COUNT), 0)
  • 数値の部分はバインド変数が適用できます

DBFluteでは、ConditionBeanのSQL関数フィルタの round() にてこれを利用しています。

数値の切り捨て

truncate() 関数を利用することで数値の切り捨てができます。

e.g. 購入数量の平均値の小数点を切り捨て @SQL
truncate(avg(PURCHASE_COUNT), 0)
  • 数値の部分はバインド変数が適用できます

DBFluteでは、ConditionBeanのSQL関数フィルタの trunc() にてこれを利用しています。メソッドは trunc() ですが、内部的に truncate() として解決されます。

日付のフォーマット

date_format() 関数を利用することでフォーマットすることができます。

e.g. 更新日時を yyyy/MM/dd 形式でフォーマット @SQL
date_format(UPDATE_DATETIME, '%Y/%m/%d')

日付の切り捨て

素直にはできないので、様々な関数を組み合わせて実現します。実現方法は一つに限りません。

e.g. 更新日時の時分秒ミリ秒を切り捨てて日付に @SQL
cast(substring(UPDATE_DATETIME, 1, 10) as date)
e.g. 更新日時の日と時分秒ミリ秒を切り捨てて日付に (月初めになる) @SQL
cast(concat(substring(UPDATE_DATETIME, 1, 10), '-01') as date)

DBFluteでは、ConditionBeanのSQL関数フィルタの日付の切り捨てにてこれを利用しています。

日付の加算・減算

date_add() 関数を利用することで加算と減算ができます。

e.g. 更新日時を一日進める @SQL
date_add(UPDATE_DATETIME, interval 1 day)
  • 数値の部分はバインド変数が適用できます
  • マイナス値を指定すると減算になります
  • day の他に、year, month, hour などが指定できます

DBFluteでは、ConditionBeanのSQL関数フィルタの日付の加算にてこれを利用しています。

テーブル定義

Identity設定

e.g. Identity設定 {MEMBER_IDにIdentity} @SQL
create table MEMBER (
    MEMBER_ID INTEGER AUTO_INCREMENT NOT NULL,
    MEMBER_NAME VARCHAR(200) NOT NULL,
    ...
)

テーブルコメントの設定

テーブルやカラムのコメントは、comment on ではなく create table に設定します。最大文字数は、テーブルは 60 文字、カラムは (約!?) 255 文字です。

e.g. テーブルやカラムのコメントを設定 @SQL
CREATE TABLE PURCHASE(
    PURCHASE_ID BIGINT AUTO_INCREMENT NOT NULL COMMENT '購入ID: 連番',
    MEMBER_ID INTEGER NOT NULL COMMENT '会員ID: 会員を参照するID。'
    PURCHASE_COUNT INTEGER NOT NULL COMMENT '購入数量: 購入した商品の(一回の購入における)数量。',
    ...
) COMMENT='一つの商品に対する一回の購入を表現する。
一回の購入で一つの商品を複数個買うこともある。' ;

ユニーク制約の特徴

NotNull の FOO カラムと、そうでない BAR カラム で複合ユニーク制約を付けた場合、"A と null" と "A と null" の二つのレコードは登録できます。つまり、この場合の二つの null はそれぞれ別の値として扱われます。

※試しに、会員の生年月日とバージョンNOに複合ユニーク制約を付けてもExampleのテストデータは正常に登録された

発行された連番を insert 実行後に取得

同じトランザクション内での実行が前提です。トランザクション外で実行しても正常な値は取得されません。 ゆえに MyISAM では利用できません。

e.g. 発行された連番を insert 実行後に取得 @SQL
select last_insert_id();

DBFluteでは、Behavior の insert() の処理の中で、このSQLを内部的に発行して採番された値を Entity に格納しています。デバッグログにもこのSQLが表示されます。

環境周り

ストレージエンジンの設定

例えば、(デフォルトのストレージエンジンとして) InnoDB を利用する場合以下のように設定します。

e.g. ストレージエンジンを InnoDB に設定 @my.cnf
[mysqld]
default-table-type=InnoDB

テーブル名のケース区別なし管理

MySQLは、Linux や Windows などの環境ごとに、テーブル名やカラム名のSQL上での大文字小文字の識別に違いがあります。 例えば、Linux では大文字小文字を区別し、Windows では区別しません。

これにより、開発は Windows で大文字小文字区別せずにSQLを書いていて、いざ結合テストなどでサーバの Linux で動かしたときにSQLが動かない、というような状況が発生する可能性があります。

環境に依存せず大文字小文字を区別しないようにするためには、my.cnf(ini) に以下のような設定をします。

e.g. SQLでのテーブル名やカラム名の大文字小文字を区別しない設定 @my.cnf
[mysqld]
lower_case_table_names=1

このようにするとMySQL内部では全て小文字で管理されるようになり、SQL文での大文字で書かれたテーブル名やカラム名が実行時に小文字に変換されて処理されます。

ただし、元の大文字小文字が判別できなくなってしまうため、その判別がどこかしらの処理で必要になるような場合は、この設定は適切ではありません。 MySQLの中での管理が全て小文字になるため、MySQLからテーブル名やカラム名をメタ情報として取得すると、全て小文字になっています。 例えばキャメルケースでテーブル名を表現しているような場合、DBFlute のような自動生成ツールで、MemberStatus が全て Memberstatus となってしまい、動作に問題はありませんが見づらくなります。そういうときは、この "lower_case_table_names" の別の値も検討する必要があるかもしれません。

トランザクション分離レベルの設定

MySQLではトランザクション分離レベルはデフォルトで "Repeatable Read" になっていますが、 他のデータベースのデフォルトである "Read Committed" に合わせたい場合があります。

e.g. トランザクション分離レベルをRead-Committedに設定 @my.cnf
[mysqld]
transaction-isolation=Read-Committed

キャラセットの設定

e.g. キャラセットを "UTF-8" に設定 @my.cnf
[mysqld]
default-character-set=utf8

SQLモード

SQLのデフォルト挙動を微調整するための様々な設定が存在します。

例えば、MySQL は以下のような group by 句に存在しないカラムを select 句に指定してもエラーになりません。(他のDBMSでは大抵はしっかりとエラーになる)

e.g. 解決できないカラムがある group by のSQL {SQLモードを指定すればエラーにできる} @SQL
select MEMBER_STATUS_CODE, MEMBER_NAME
  from MEMBER
 group by MEMBER_STATUS_CODE;

こういった "安全ではない割り切り" が多数存在します。SQLモードを設定することでそれらをしっかり実行時エラーにすることが可能です。 SQLモードの設定は、my.cnf(ini) に以下のように設定することが可能です。カンマ区切りで複数指定が可能です。但し、"a, b" というように空白を一つ開けるのは不可なので注意して下さい("a,b" と指定すること)。

e.g. SQLモードの設定 {SQLモードを指定することによりエラーになる} @my.cnf
[mysqld]
sql_mode="ONLY_FULL_GROUP_BY,STRICT_ALL_TABLES,PIPES_AS_CONCAT,ANSI_QUOTES,NO_ZERO_DATE,NO_ZERO_IN_DATE,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

DBFluteであれば、型の解決などは自動生成時点で担保されるため、あまり厳密にせずとも問題は発生しにくいですが、 それでも設定しておいた方が良いと思われる代表的なSQLモードを挙げておきます。

ONLY_FULL_GROUP_BY
間違ったGroupByがちゃんとエラーになる
PIPES_AS_CONCAT
SQL上の文字列連結で '||' が利用可能になる
ANSI_QUOTES
ダブルクォーテーションがバッククォートと同様の識別子となる

デリミタデータの取得

テーブルのデータをデリミタデータ(TSVやCSVなど)として出力するコマンドがあります。

e.g. キャラセットを "UTF-8" に設定 @SQL
select * from MEMBER
into outfile "/tmp/MEMBER.tsv"
fields terminated by '\t'
enclosed by '"';

データの中のダブルクォーテーションは、バックスラッシュ "\" でエスケープされ、改行の前にも "\" が付与されます。よって、DBFlute の ReplaceSchema のデータ登録のための TSV を作成する場合は、convertValueMap.dataprop でこれらをうまく置換して仕様を合わせる必要があります。

e.g. 実際に試してデータ登録したときの設定 (足りない可能性もある) @convertValueMap.dataprop
map:{
    ; $$LINE$$ = map:{
        ; \\\\ = \\
        ; \\" = ""
    }
    ; $$ALL$$ = map:{
        ; \N = 
        ; $$empty$$ = $$empty$$
        ; contain:\\\n = \n
    }
}