Liner Note

情報(ユーザー中心デザイン・ユーザビリティ)と技術(ウェブプログラミング・ウェブサービス)についてのメモ書き

要約:リレーショナル・データベースが扱いづらい問題について、RDB初心者が思案を巡らしてみた記事

この記事は書いている内に、問題解決の糸口が見えてきたけど、問題だと思っているところをはっきりさせるために、そのまま書いた記事です。

以下に、その思考ログを書いておきます。

RDBが使いづらい理由

最近、MySQLやSQLiteなどのリレーショナル・データベースを使い始めたのですが、XMLをどうにも使いづらいという感は抜けません。そんなこともあって先日は擬似XMLデータベースなんてへんてこなものを作ったりしたのですが、気がついてみればその理由をあんまり書いてなかったので、使いづらさに慣れちゃう前に理由を整理してみました。

スキーマを事前に定義しておかないといけない

開発中にはデータベースのデータ構造が変化すると言うことは良くあります。そのたびに、スキーマ定義をカリカリ書き換えているのは面倒ですから、データベースには速度だけでなく、柔軟性・拡張性が求められることになります(これはリレーショナルデータベースとXMLデータベースとの対比で良く言われます)

データ型として配列が使えない

簡単に言えば、一つのレコードに複数の値を設定できないということです。ブログのタイトルのようにキーと値が1:1で対応している場合は問題ないのですが、例えばこの記事のカテゴリのように、あるキーに複数の値が対応する(1:n)ということはよくあることです。しかし、どうもマニュアルを見る限りは、MySQLに複数の値を格納できる型、つまりは配列型はないようなのです。

この場合、一般的にはシリアル化(serialize関数)して文字列として処理→DBから取り出した後に復元(unserialize関数)(PC用語で言うと圧縮、解凍みたいなものですね、サイズは変わりませんが)という方法が採られているようですが、一手間かけさせられているというか、言語側の仕様に依存しているようでどうにも納得いきません。

この項、シリアライズが一般的と書きましたが、いくつかご指摘をいただきまして、標準的には第一正規化(つまり別テーブル化)するのが一般的だそうです。まぁ、どちらにしても一手間かけなければいけないことに代わりはないようなのですが、勉強になりました。

ちなみに、この手順をコードにしてみるとこんな感じ(例:商品の名前・価格・カテゴリについて書いてあるデータベース)

PHPソースコード
<?php
/** データを挿入するとき **/
$category = array(
  "音楽再生機器" => "MP3プレイヤー",
  "iPod" => "本体",
  "スマートフォン" => array("Apple","ソフトバンクモバイル")
);
$vars = array(":id"=> 10, ":name"=> "iPhone", ":price"=> 200000, ":category"=> serialize($category));
$sql = "INSERT INTO 'mytable' (id, name, price) VALUES (:id, :name, :price)";
$request = $db->prepare($sql);
$request->execute($vars);
 
// (中略) 
 
/** データを取り出すとき **/
 
// SELECT文の発行とかはすっとばします
$names = array("id", "name", "price", "category");
while ($row = $request->fetch(PDO::FETCH_ASSOC)){
  foreach ($names as $name){
    $v = $row[$name];
    // gettype関数先生は全部stringと仰るので
    // 泥臭く文字列としてシリアライズされているか判定
    $results[$i][$name] = (preg_match("/^a:[0-9]+:{/", $v)) ? unserialize($v) : $v;
  }
  $i++;
}
 
/*
  $results = array(
    [0] => array(
      "id"=>10,
      "name"=>"iPhone",
      "price"=> 200000,
      "category"=>array(
        "音楽再生機器" => "MP3プレイヤー",
        "iPod" => "本体",
        "スマートフォン" => array("Apple","ソフトバンクモバイル")
      )
    ),
    [1] => array(
    ...
    )
  );
*/
?>

ちなみに、SET型はユーザ定義型(あらかじめ入る内容を定義しておかないと行けない)なので、配列的に使うには無理がありますし、join関数を用いた単純結合は多次元配列を処理できません。

プログラミング言語との連携が弱い

データベースはプログラムとセットにして使うものなのに、その連携が弱いんじゃないかなと思うのです。

例えば、先の例で商品データベースを使いましたよね。再掲すると…

PHPソースコード
<?php
$category = array(**省略**);
$vars = array(":id"=> 10, ":name"=> "iPhone", ":price"=> 200000, ":category"=> serialize($category));
$sql = "INSERT INTO 'mytable' (id, name, price) VALUES (:id, :name, :price)";
$request = $db->prepare($sql);
$request->execute($vars);
?>

これ、個人的にはこう書きたいんですね(連想配列を投げる)

PHPソースコード
<?php
$vars = array("id"=> 10, "name" => "iPod nano", "price" => 200000, "category"=>$category);
$request = $db->insert("mytable", $vars);
?>

今回は後者を前者に置換するラッパ関数を書けば良い話ですが、さきほどの配列型の問題と絡めて、こういうのがデフォルトになってくれるといいなと。

まぁ、あと細かいことを挙げれば

  • データの挿入(INSERT)と更新(UPDATE)でクエリの書式が違う
  • SQLインジェクションが構造的に発生する
  • データがバイナリファイルである(これはちょっとイチャモンぽいですが)

とありますが、冗長になるので深くはつっこまないことにします。

O/Rマッパーがいいらしい?

検索していると、連携問題はどうやらオブジェクト関係マッパー(O/Rマッパー)と呼ばれるもので解決できそうな感じです。

このO/Rマッパーというのは、要するに人間がデータベースを触りやすくなるように、プログラミング言語のお作法で違和感なくデータベースがいじれるようにするような仕組みのようです。PHPのフレームワークの一つであるCakePHPでは、ActiveRecordという考え方(デザイン・パターンとか言うらしい)を基にしたO/Rマッパーが実装されている模様。

配列型問題はどうだろう…定義からして解決できるぽい感じはするのですが、具体的な記述が見つけられません。あと、最初にデータベース定義が必要なところはどうも解決してないっぽいですね。むー

O/Rマッパーは問題を解決する?

じゃあ、O/Rマッパーを使えばいいのかというとそれもちょっと疑問なんですよね。僕が抱えてる問題はちょっとは解決するんですが、別に思うこともありまして…。で、これには2つの理由があります。

  1. 初心者は結局、SQLを学ばないといけない
  2. O/Rマッパは対処療法である

まず、「じゃあ初心者ってO/Rマッパ使うのか」という疑問です。PHPの入門書でも、だいたいPEAR::MDB2の使い方を解説するか、PDOの使い方を解説するくらいが現状なわけで、結局はSQLの書き方やお作法を学ぶことになるでしょう。

Wikipediaの「O/Rマッパー」の項にはオブジェクト関係マッピングは、オブジェクト指向と関係モデルのインピーダンス・ミスマッチ問題の対症療法にすぎないとも言われていると書かれていますが、これはその通りで、上記の問題が起こるのもこの問題があるからでしょう。

つまり、O/Rマッパーはプログラム言語とSQL言語の翻訳者でしかないわけで(断言してますけど、自信はないです)、SQL自身が関係モデルを取り、SQL言語を話している限りは、その問題の根本が解決したとは言えないのです。とはいえ、オブジェクトデータベースやXMLデータベースは、PerlやPHPなどプログラム初心者が手を出しやすい軽量言語では実質使えない状況です。

初心者にとって、プログラム言語とSQL言語のバイリンガルにならざるを得ない状況があって、それがプログラミングの壁を厚くしているような感じがします。まぁ、現状はO/Rマッパー(を搭載したフレームワーク)が最適解ぽいので、この問題は、「現状は初心者こそCakePHPを使うべき」が落としどころになりそうです。しかし、入門書にフレームワークの記述は難しいだろうなぁ。

言及されたURL

以下で言及されました(ありがとうございます)
ORマッパーとか | 眠る開発屋blog

SQLのコストについて。確かにSQL学習上のコストはそれほど高く付くわけではないのですしこの先数年も流通している知識だとは思うのですが、それが本来必要なコストなのかどうかということをSQL初学者として書いておきたかったのでした(できるならデータベースのお作法を気にせずに=透過的に、扱えるようになるべきでしょうし)

9784839924317

キーワード:

似たもの記事

読者の皆さんの反応サイト内コメントの更新情報(RSSフィード)

読者のコメント

3

ブックマークコメント

7

他サイトの関連記事

0

読者のコメント

  1. お名前

    通りすがり

    投稿日時
    2009年01月22日
    21時ごろ
    Comment No
    #1

    問題はPHPを覚えるSQLを覚えるという事では無く、設計的な観点からどのようにデータをモデル化するか、という事ではないでしょうか。
    モデルに従って構造化されたデータを意識しない/したくない(たとえばリレーショナルモデルの構造は「お作法」でしか無く面倒なだけだと思う)のであれば、フラットなテキストデータだけを扱うのが一番かも知れません。これならほぼ完全に「自分の知っている言語」だけで扱えますし。
    或いは独自のコンパクトなデータ構造を実装するとか。事実上どんなデータタイプでも扱える筈です。
    何れにしても、O/RマッパはSQLを書きたく無いという理由だけで利用するものでは無いように思いますが…

  2. お名前

    ゴット

    投稿日時
    2010年06月13日
    11時ごろ
    Comment No
    #3

    RDBの欠点として以下の事を挙げていますが、
    「一つのレコードに複数の値を設定できないということです。」
    これは大きな間違いでRDB最大の功績です。
    正規化されることにより、柔軟なデータ構造の拡張性が得られ
    同じソースコードを配列分、何回も書かなくてよくなることによる
    ソースコードのメンテ性の向上も実現されました。
    直感的なデータの扱いやすさを理由に非正規形にするのは、
    COBOL屋の発想です。

はてなブックマークでつけられたコメント

acidgraphixさんのプロフィール画像  acidgraphix
O/Rマッパーの使いどころ
closerさんのプロフィール画像  closer
cti
Knoaさんのプロフィール画像  Knoa
一般的にはシリアル化←ねーよwwww / つ正規化
ramsさんのプロフィール画像  rams
関係ないけど、最近ってER図書かないのかな?
fukkenさんのプロフィール画像  fukken
"しかし、どうもマニュアルを見る限りは、MySQLに複数の値を格納できる型、つまりは配列型はないようなのです"それ第一正規化で(ry
terazzoさんのプロフィール画像  terazzo
思考の過程を記録出来るのがWebの良い所。
/こういう問題で悩むことは、検索や永続化の処理の本質について気付かせてくれるのでとても良いと思う。
fuktommyさんのプロフィール画像  fuktommy
RDBは本来はデータ格納装置じゃないらしいし、なんかこう便利なものはないかねえ。
ついついNFS代わりに、複数のホストから参照できるディスクとして使っちゃうけど。

他サイトの関連記事

トラックバックはまだ寄せられていません


トラックバックとは
この記事に言及したサイトをこちらに掲載する仕組みをトラックバックと言います。ここでは、このサイトに頂いたトラックバックを一覧表示しています。
トラックバックしてくださる方へ
この記事への言及がない記事など、トラックバック受信方針に沿っていないものは、読者にお見せしても仕方ないこともあり削除させていただいることをご了承ください。
トラックバックを受け取るためのURI

コメント書き込みフォーム

  • メールアドレスはウェブ上で公開したり、連絡以外で使うことはありません
  • コメントを公開したくないが、作者に連絡を取りたい場合は メールで連絡してください
  • 本文中にHTMLコードは使用できません(URLはそのままお書きください)