php
2011年09月27日
MySQLにおけるnullの取り扱い
前提:
担当者名を入れるフィールド「charge」があり、担当者名が入っているかnullかのどちらかです。
またアクセス者名を入れる変数$userがあり、これは必ずアクセス者名が入っています。
ここで、「自分が担当者でない」データを検索するとき
〜 and charge != '$user' and 〜
としたところ、chargeがnullのデータは一切ヒットしなくなりました。
Google先生によると出てきた解は、MySQLの比較は比較物がnullの時は必ず「偽」を返す、というものでした。(これも嘘というか私の解釈違いな訳ですが)
そこで次のように変更してみます。
〜 and not(charge = '$user') and 〜
偽のnotなのでこれでうまくいくと思いましたが、結果は先ほどと全く同じ。どうもnullの扱いがよくわかりません。
さらに調べていくと…、ありました。「nullセーフ」というんだそうです。
MySQLの比較演算子「=」はnullセーフではないとのこと。「=」に相当するnullセーフな比較演算子は「<=>」なんだそうです。
つまり私の期待した答えを得られる式は
〜 and not(charge <=> '$user') and 〜
ということです。
正確な仕様は、
- MySQLの真偽は「真=1」「偽=0」と考えるとわかりやすい。
- 通常の(nullセーフでない)比較演算子は、比較対象がnullの時必ず「null」を返す。(nullは「0」(つまり偽)ではない。)
- nullセーフな比較演算子「<=>」で期待する結果が得られる。
ということの様です。(参考資料:MySQL 6.3.1.2. 比較演算子)
まとめてみると
chargeの中身 | 期待値 | charge != '$user' not(charge = '$user') | not(charge <=> '$user') |
アクセス者名 | 偽 | 偽 | 偽 |
他のユーザ名 | 真 | 真 | 真 |
null | 真 | 偽 | 真 |
こんな感じでしょうか。
ここでいう「nullセーフな比較」という概念そのものを知らなかった(全ての比較演算子がnullセーフだと思っていた)ため、ここを疑うまでに結構かかりました。
思い込みって怖いですね。
静かで安くてコストパフォーマンス最高。
Linuxサーバに最適。
2011年09月22日
PHPでのMIMEエンコード、デコードで文字化け
phpからメールを送る際に、ちょっとはまったので覚え書きを。
元々の思い込みとしては、
- メールヘッダの文字コードはJIS(ISO-2022-JP)に変換
Subject以外のヘッダの日本語部分は全てMIMEエンコードする
をきちんとやれば文字化けはしないはず、ということ。
…のつもりが何故か文字化けします。
構文は、例えば
$headers = "From: ".mb_encode_mimeheader(mb_convert_encoding("氏名", 'ISO-2022-JP', 'EUC-JP'))." <メールアドレス>\n";
な感じ。
(ちなみに訳あってコードはEUC-JPで書いてます)
ところがこれで化けます。
結論としては、
mb_language('japanease');
mb_internal_encoding('EUC-JP');
$headers = "From: ".mb_encode_mimeheader("氏名")." <メールアドレス>\n";
でいい様です。
mb_encode_mimeheader()、mb_decode_mimeheader()共に受け渡しの文字コードは指定できないわけですが、これがいわゆる「内部エンコーディング指定」で行なわれている様なんです。
つまり、内部エンコーディングに指定した文字コードで受け渡しをすれば適切に処理してくれる、ということ。
こういうブラックボックスな仕様はやめてほしいものです。
文字化けに関する基本的な考え方としては以下がとても参考になります。
PHPの文字化けを本気で解決するhttp://hain.jp/index.php/tech-j/2007/02/13/p125
何よりも問題の本質は、日本語文字コードが乱立していることなんでしょうけどね…。
なににせよ、日本語処理には悩まされることが多いですね。
--
これがないと始まりません。
かなり痛んできたので、そろそろ買い直そうかと。