MySQL の IN 句における NULL の扱いについて

MySQL の IN 句における NULL の扱いがよく分からなかったので
調査してみた時のメモ

まずは基本

  • 値が IN 句に含まれる場合は 1 を返す
mysql> SELECT 5 IN (1, 2, 3, 4, 5);
+----------------------+
| 5 IN (1, 2, 3, 4, 5) |
+----------------------+
|                    1 |
+----------------------+
1 row in set (0.00 sec)
  • 値が IN 句に含まれない場合は 0 を返す
mysql> SELECT 6 IN (1, 2, 3, 4, 5);
+----------------------+
| 6 IN (1, 2, 3, 4, 5) |
+----------------------+
|                    0 |
+----------------------+
1 row in set (0.00 sec)

NULL 利用

  • 値が IN 句に含まれる場合は 1 を返す
mysql> SELECT 5 IN (1, 2, 3, 4, 5, NULL);
+----------------------------+
| 5 IN (1, 2, 3, 4, 5, NULL) |
+----------------------------+
|                          1 |
+----------------------------+
1 row in set (0.00 sec)
  • 値が NULL を含んだ IN 句に含まれない場合は NULL を返す
mysql> SELECT 6 IN (1, 2, 3, 4, 5, NULL);
+----------------------------+
| 6 IN (1, 2, 3, 4, 5, NULL) |
+----------------------------+
|                       NULL |
+----------------------------+
1 row in set (0.00 sec)
  • NULL が IN 句に含まれない場合は NULL を返す
mysql> SELECT NULL IN (1, 2, 3, 4, 5);
+-------------------------+
| NULL IN (1, 2, 3, 4, 5) |
+-------------------------+
|                    NULL |
+-------------------------+
1 row in set (0.00 sec)
  • NULL が IN 句に含まれる場合は NULL を返す
mysql> SELECT NULL IN (1, 2, 3, 4, 5, NULL);
+-------------------------------+
| NULL IN (1, 2, 3, 4, 5, NULL) |
+-------------------------------+
|                          NULL |
+-------------------------------+
1 row in set (0.00 sec)

ダメだ。意味が分からない。。


なお、リファレンスにはこう書いてあった

expr が、IN リストのどれかの値と等しい場合は 1 を戻し、それ以外では 0 を戻します。すべての値が定数の場合、expr のタイプに基づいて評価し、分類します。その際の項目の検索は、バイナリ検索を使って行われます。これはつまり、IN は、IN 値のリストがすべて定数で構成されている場合、非常に速いということです。もしくは、項11.1.2. 「式評価でのタイプ変換」 にあるルールによってタイプ変換が実施されますが、すべての引数に適用されます。

mysql> SELECT 2 IN (0,3,5,7);
        -> 0
mysql> SELECT 'wefwf' IN ('wee','wefwf','weg');
        -> 1

引用符で括られた値 ( ストリングなど ) と括られていない値 ( 数字など ) の比較ルールは異なるため、IN リストの引用符で括られた値と、括られていない値を決して混同しないでください。タイプの混同は、上記の理由により、結果の矛盾の原因になることがあります。例えば、IN 式を次のようには書かないでください :

SELECT val1 FROM tbl1 WHERE val1 IN (1,2,'a');

正しい書き方はこのようになります :

SELECT val1 FROM tbl1 WHERE val1 IN ('1','2','a');

IN リストの値の数は、max_allowed_packet 値によってのみ制限されます。

SQL の標準に準拠するため、IN は、左側の式が NULL である場合だけでなく、リストに一致するものがない場合、また、リストの式のひとつが NULL である場合にも、NULL を戻します。

IN() 構文は、ある種の副問い合わせを書くのにも使用できます。項12.2.8.3. 「ANY、IN、そして SOME を持つサブクエリ」 を参照してください。

http://dev.mysql.com/doc/refman/5.1/ja/comparison-operators.html#operator_in

HTML テンプレートで Web サイト簡単構築

HTML テンプレートで Web サイトを簡単構築してみました。


◆構築の経緯
友人に所属しているダンスチームのケータイサイトを構築してほしいと頼まれました。
それも二日で!


開発ナメすぎと思いましたが、次の条件で開発を引き受けることに。

  • データベースは使わない
  • デザインは行わない(幸い少しデザインが出来る別の友人がいたのでそいつに任せる)
  • テストは行わない(友人本人にさせる)


ただ、動的にコンテンツを出力に関しては曲げられないとのこと。
デザインを別の友人に頼めたのはせめてもの救い。


◆テンプレートエンジンの利用
そういう訳で

  • 情弱エンジニア
  • 素人デザイナー

の二人での開発となりました。


そこでやっぱり考えるのが
「どうやってシステムとデザインを分けるのか」


二日しかないのでフレームワークに手を出すのも気が引ける。
ということで簡単に使えるテンプレートエンジン(があれば)を利用することにしました。


で、調査していると
HTML テンプレート
というテンプレートエンジンが簡単に使えるっぽい。


本体のソースコードも説明文を除けば全300行というシンプルさだし、
使える機能も十分事足りるレベルだったので利用することにしました。

PHP でリクエストヘッダを取得する方法

PHP でリクエストヘッダを取得する方法です。


PHP にはリクエストヘッダを取得してくれる関数があります。
getallheaders 関数と apache_request_headers 関数です。


それぞれはエイリアスのため使い方、使い勝手も同じです。


▼リクエストヘッダを取得するサンプルプログラム
(主に実装中にヘッダを確認したい時とか用)


※ MAIL_TO (メールの宛先)と SUBJECT (メールの件名)は
正しく指定して使ってください。

<?php

define("MAIL_TO", "sample@example.com");
define("MAIL_SUBJECT", "GET Headers!");

$headers = getallheaders();

$outputs = array();

foreach ($headers as $key => $val) {
    $outputs[] = $key. ': '. $val;
}

mail(MAIL_TO, MAIL_SUBJECT, implode("\r\n", $outputs));

?>


PC ブラウザですと Firefox のアドオン「 Live HTTP headers 」を使えば確認出来ますが、ケータイのヘッダはこの方法でないと取得出来ないんですよねー


そしてケータイの小さな画面で見るのはちょっと辛いので、メールで送信するようにしています。


大したことないレベルのコードですが、
ヘッダを取得したい時など毎回書くのは手間なので残しておきたいと思います。

sim 差し替えでガラケーとスマホを使う

最近、「そろそろスマホを持ってないと本当に時代に取り残されてしまう!」という危機感を抱くようになりました。


仕事柄、一般の方より携帯を触る機会が多いのですが
最近はほとんどスマホを触ってます。


ガラケーの開発が縮小していて、スマホの開発がますます増えていってます。


会社の人はほとんどが私用ケータイをスマホにしていますし、
ガラケーの人でも「次変える時はスマホ!」なんて言っている人が多いです。


社会を見ても携帯キャリア 3 社ともスマホを押していますし、
個人的にはガラケーからスマホへの移行がますます加速し、
遅くとも 3 年後には若者の大半がスマホを持っている社会になるんじゃないかと見ています。


そんな状況で、「そろそろ手に入れないとまずいな」と思っている訳です。


ただ、僕は今年の初めにガラケーからガラケーに機種変したばかりなので
2 年縛りから解放されないんですよね。


かと言って、2 台持ちするのは資金面で結構痛かったりする。


1 ヶ月 7, 8 千円と聞くと「大したことないか」と思うのですが、
1 年で 10 万円と聞くと、えらい高く感じてしまうんです。


そんな訳で
今のガラケーに使っている sim カードを
ガラケースマホで差し替えて使えないかと睨んでいる訳です。


で、ちょっと調べてみました。



僕は au を使っていて、
WEB サービスは確かダブル定額ライトを使っていた気がします。


毎日ケータイでブログ見たり twitter 見たりしてるので
ダブル定額でも良いんだけど、確かライトだったはずです。


で、ダブル定額ライトの場合
下の図の通りスマホを使うと上限が「4,410 円 → 5,985 円」になるってだけの話らしい。



特にプランを IS フラットに変えなければいけないってことでもなさそうだ。


スマートフォン単独利用時」という言葉がひっかかるが、
要は携帯をテザリング機能を使ったりして PC の通信に利用しなければ良い
って話です。

Wikipedia のデータ取得方法について

ちょっと Wikipedia から情報を取得してコンテンツを作りたいと思ったので調べてみた。


ちょっとしたデータなら SimpleAPI という API で取得可能ですが、
取得出来るデータはかなり少なく、コンテンツを作る上では不向き。


で、何とか Wikipedia のデータをフルで取得出来ないかと考えている。


参考になったのは YARETOKO さんの記事


まとめるとポイントは 2 つあるようです。

  1. Wikipedia API でデータを取得
  2. 取得したデータを Text_Wiki_Mediawiki でパースする


まず、Wikipedia で用意されている(?) API を使って Wikipedia のデータを取得する。
ただし取得したデータは XML 表記かつ Wiki 表記となっているので、次に Text_Wiki_Mediawiki という PEAR のライブラリを使ってパースする。
簡単に説明するとそんな感じ。

1. Wikipedia API でデータを取得


Wikipedia API については 気軽に開発メモさんの記事 が詳しかった。


なんでも

http://ja.wikipedia.org/wiki/特別:データ書き出し/[キーワード]
(※もちろん日本語は URL エンコードして使います)

の URL を叩けば記事が XML で取得出来るとのこと。


大好きなナインティナインさんの記事を取得してみることにする。

http://ja.wikipedia.org/wiki/%E7%89%B9%E5%88%A5:%E3%83%87%E3%83%BC%E3%82%BF%E6%9B%B8%E3%81%8D%E5%87%BA%E3%81%97/%E3%83%8A%E3%82%A4%E3%83%B3%E3%83%86%E3%82%A3%E3%83%8A%E3%82%A4%E3%83%B3
(※ http://ja.wikipedia.org/wiki/特別:データ書き出し/ナインティナイン

こんな感じでしょうか。
で、ブラウザで確認すると記事が XML で確かに取れてる!


これを HTTP で取得するプログラムを書けば良い訳ですね。

サンプルプログラム


get_wiki_data.php

<?php

/**
 * Wikipedia からデータを取得するプログラム
 */

// Wikipedia API の URL
define('WIKIPEDIA_API_URL', 'http://ja.wikipedia.org/wiki/%E7%89%B9%E5%88%A5:%E3%83%87%E3%83%BC%E3%82%BF%E6%9B%B8%E3%81%8D%E5%87%BA%E3%81%97');

// 取得するタイトル
$search_title = 'ナインティナイン';

// ストリームコンテキストの生成
$stream_context = stream_context_create(array(
    'http' => array(
        'method' => 'GET',
        'header' => 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
    ),
));

// データの取得
$wiki_data = file_get_contents(WIKIPEDIA_API_URL. '/'. urlencode($search_title) , false, $stream_context);

// 結果出力
$wiki_data_encode = mb_detect_encoding($wiki_data);
header('Content-Type: text/html; charset='. $wiki_data_encode);
echo $wiki_data;

?>


データを取得するプログラムを作る上でのポイントが UserAgent を送信すること!


YARETOKO さんの記事 にもある通り、Wikipedia は UserAgent がリクエストに付与されていないと受け付けないらしい。ボット対策なのかな!?


で、ぎじゅつやさんの記事 を参考に作ってみたのが上。


早速実行してみる。



※出力結果をエディタで開いた


ちゃんと取得出来た!

2. 取得したデータを Text_Wiki_Mediawiki でパースする


続きはまた今度

開設してみた

日頃システム開発を行っていると
色々な方の記事に助けられることがよくある。


本当に感謝のしっぱなしだが、
今度は自分が調べたり、検証した結果が
他の人の参考になればと思いブログを開設してみることにした。


これまでブログは何度か開設したことはあるけれど
技術ブログとしては今回が初めて。


三日坊主で終わらずに
長続きさせられると良いなぁ。