MySQLのエンジンを調べる

MySQLで利用可能なエンジンでは、MyIsam、InnoDBが有名ですよね。
僕は、この2種類しか使ってないですが、MySQLでは他にも利用可能なエンジンがあります。自分が使っているMySQLで、どのエンジンが使えるのかを調べようと思ったら、どうするんでしょう?

MacにインストールしてたMySQLならroot権限もあるので、MySQLテーブルとかを見てみても、それっぽいところは見当たらず。(見逃しているだけかもしれません)そこで、会社のサーバ管理の方に教えてもらったら、
mysql> show engines
というのがMySQL5だと使えるそうです。

教えてもらった後に検索したら、MySQL4でも
mysql> show variables like ‘have_%’
というコマンドで、利用可能なエンジンを調べることができました。

書かれた日付は古いですが、参考になった記事はこちらです。
http://itpro.nikkeibp.co.jp/article/COLUMN/20050922/221608/

Zend_Db_Table_Rowsetを拡張する

Zend_Db_Table_Rowsetのデータを削除したり、一括で更新したりするのに
foreach使っていたんですが、めんどうだなーと思って、拡張を考えました。

http://framework.zend.com/manual/ja/zend.db.table.rowset.html#zend.db.table.rowset.extending
上記を参考にして、以下のクラスを追加

class MyRowset extends Zend_Db_Table_Rowset_Abstract
{
    public function delete()
    {
        if (!$this->valid()) { return; }
        foreach ($this as $row) { $row->delete(); }
    }

    public function save()
    {
        if (!$this->valid()) { return; }
        foreach ($this as $row) { $row->save(); }
    }

    public function __set($key, $value)
    {
        if (!$this->valid()) { return; }
        foreach ($this as $row) { $row->{$key} = $value; }
    }
}

まだ動かしてないので、うまくいくかわからないけれど、メモとして残します。

あと、書き込み操作連鎖も初めて使ってます。
http://framework.zend.com/manual/ja/zend.db.table.relationships.html#zend.db.table.relationships.cascading

DBはMySQLで、外部キー制約を付けようと考えたのですが、対象テーブルにないキーが入ってしまうことがあるので、つけることができずに、上記の機能を利用することを考えました。
今までは、MySQLで外部キーつける権限がない時に使えるくらいの認識だったんですが、都合がいい部分だけ外部キー制約つけれるかもってことで、これは使えるんじゃないかと思います。

MySQLのViewを試してみたい

Viewテーブルについてはこちら
http://dev.mysql.com/doc/refman/5.1/ja/create-view.html

これ使うと、left joinを使っている部分がレコード数が多くなって、
取得が遅くなっている現象を回避できるんじゃないかと思ったのです。

利用しているMySQLが4系なので、5系にバージョンアップしてから。
そうしたら、ストアドプロシージャも試してみたい。
http://www.atmarkit.co.jp/flinux/special/mysql5/mysql5d.html

以前、Oracleを使ってたときは、性能改善でSQLをストアドプロシージャ化する
ってことをやる担当の人もいたけど、MySQLではどうなんだろうか。

MySQLでの発見

select
  t1.column_a,
  t2.column_b
from
  table_a as t1
    left join table_b as t2 on (t1.hoge = t2.hoge and t2.make_date <= 'xxxx')
where
  t2.column_b is null

こんな感じで、left joinしたテーブルに対しての条件を、
更にwhere句で追加できるとは知らなかったです。
勝手にできないものだと思っていました。

この場合、t2.column_bがnullではないものは、left join on で指定してしまえばOKなのですが、
nullの場合、カラムの値がnullでなくても、make_dateの条件に合致しなければ、nullが
取得されるので、今までは取得結果をループで回してnull以外のデータをはじいてました。

これで、無駄なソースが減る場所がいくつかあったので、メモです。

MysqlのIFとIFNULL

マニュアルはこちら
http://dev.mysql.com/doc/refman/4.1/ja/control-flow-functions.html

left joinでNULLのときの表示値設定や、カラムの値によって、レコード数をカウントしたり
使い勝手はいいと思うし、実際に使ってみたらとても便利。

case ~ then もいいけど、elseが必要になる場合ってあまりないから、
ifの方がSQL文も短くですっきりしますね。

今更findParent~

Zend FrameworkのDbテーブルリレーションを使って、初めて親テーブルの
データを取得したんですが、返り値ってZend_Db_Table_Rowなんですね。

子テーブルのデータをとってくるときと一緒で、Zend_Db_Table_Rowsetの
つもりで current() を呼び出してエラーになってしまっていました。

子は複数だけど、親は1つっていう考えになってるんですかね。

ほーって思ったので、メモしておきます。

fetchRowとfetchAllの違い

fetchRow、fetchAllどちらもDBから行を取得するメソッドです。

相違点は以下

fetchRow
 1. 単一の行を取得
 2. 返り値の型は、Zend_Db_Table_Row
 3. 0件取得の場合は、nullを返却

fetchAll
 1. 複数の行を取得
 2. 返り値の型は、Zend_Db_Table_Rowset
 3. 0件取得の場合は、Zend_Db_Table_Rowsetを返却

注目するところは、3です。
fetchAllは、0件でもZend_Db_Table_Rowsetを返却します。
なので、取得の有無で、nullかどうかでは判定できません。

私はどちらもnullが返ってくると勘違いしていたので、ミスりました。

Zend_Db_Tableでのinsert方法の違い

Zend_Db_Tableクラスを使った行のinsert方法は2つ。

1. createRow()を使うもの
2. insertメソッドを使うもの
※$tblは、Zend_Db_Table_Abstractを継承したDbアクセスクラスです

1. はこんな感じ

$row = $tbl->createRow();

$row->password = xxx;
$row->create_on = new Zend_Db_Exp(‘now()’);
$row->save();

2. はこんな感じ

$params = array(
 ’password’ => ‘xxxx’,
 ’create_on’ => new Zend_Db_Exp(‘now()’)
);

$tbl->insert($params);

両者の違いは、設定しなかったカラムの処理方法にあります。
値を設定しなかったカラムに対して、1.はnullを設定し、2.は何も処理をしません。

これはどういうことかというと、1.の場合は、DB設定にてdefault値を設定しても、
行をinsertした後には、カラムの値はnullになっているということです。
これは、結構不便に感じます。

1.は更新時と書き方がほぼ一緒なので覚えやすいのですが、上記default値のことを考慮すると、
insertメソッドを使う方法が、予想外の問題が発生しにくいのではないかと思います。

ログインチェックをプラグインにて行う2

今回の認証クラスでは、DBを使って認証処理を行っています。
サンプルソースは以下になります。

説明を日本語で書くと、余計にわかりづらいくなってしまいましのたで、
コードとコメントを見て理解していただけたらと思います。

Zend_Loader::loadClass('Zend_DB_Table_Abstract');
Zend_Loader::loadClass('Zend_Auth_Adapter_DbTable');
Zend_Loader::loadClass('Zend_Auth_Storage_Session');

class authModel
{
 private $_authStorage;

 public function __construct()
 {
  $config = new Zend_Config_Ini('config/system.ini', 'default');
  // ログイン情報を格納するセッションを指定
  $this->_authStorage
   = new Zend_Auth_Storage_Session($config->session);
 }

 public function login($post)
 {
  $db = Zend_Db_Table_Abstract::getDefaultAdapter();
  $authAdapter = new Zend_Auth_Adapter_DbTable($db);
  $authAdapter->setTableName('Accounts')
         ->setIdentityColumn('login_id')   // IDカラムを指定
         ->setCredentialColumn('password'); // 認証用カラムを指定

  // 上記で指定したカラムに、認証したい値を設定
  $authAdapter->setIdentity($post['login_id']) 
        ->setCredential($post['password']); 

  // 認証実行
  $result = $authAdapter->authenticate();

  if ($result->isValid()){
   // 認証OKとなったレコードから、login_idとstatusを取りだす
   $row = $authAdapter->getResultRowObject(array(login_id, status));
   // ログイン情報格納用セッションに取得した情報を設定する
   $this->_authStorage->write($row);
  }

  return $result;
 }

 public function logout()
 {
  $this->_authStorage->clear();
 }

 public function isAuth()
 {
  return (isset($this->get()->login_id) && isset($this->get()->status));
 }

 public function get()
 {
  return $this->_authStorage->read();
 }
}

ログイン情報を設定するセッションに、デフォルト(Zend_Auth)を利用したり、
セッションに設定する情報が、認証に利用したID(上記だとlogin_id)だけでいい場合は、
より簡潔に記述することができます。

デフォルトのセッションを利用する場合は、コンストラクタ内の処理は必要ありません。
セッションに設定する情報が、認証に利用したIDのみでいい場合は、
認証処理を実行してOKだった場合に、自動的に設定されていますので、
わざわざ設定する処理を記述する必要はありません。

DBアダプタ作成処理をプラグインにて行う

Dbハンドラの生成をmodelクラスで行っていたのですが、
複数のモデルクラスを使う処理の場合もあるので、どこか1か所に
生成処理をまとめられないかなと思って、たどりついたのがプラグインでした。

ソースは、以下

Zend_Loader::loadClass('Zend_Controller_Plugin_Abstract');

Zend_Loader::loadClass('Zend_Db');
Zend_Loader::loadClass('Zend_Db_Table_Abstract');
Zend_Loader::loadClass('Zend_Config_Ini');

class Zend_Controller_Plugin_DbAdapter extends Zend_Controller_Plugin_Abstract
{
 public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
 {
  // DBアダプタ生成
  $config = new Zend_Config_Ini('config/system.ini', 'default');
  $db = Zend_Db::factory($config->database->adapter,
              $config->database->params->toArray());
  $db->setFetchMode(Zend_Db::FETCH_ASSOC);

  // Db Tableクラス利用DBアダプタ設定
  Zend_Db_Table_Abstract::setDefaultAdapter($db);
 }
}

こうやっておけば、どこのクラスでも

Zend_Loader::loadClass('Zend_Db_Table_Abstract');

$db = Zend_Db_Table_Abstract::getDefaultAdapter();

とすると、Dbアダプターが取得できます。
Dbアダプターを作成するモデルクラスを基底クラスとして、作っていたのですが、
こちらの方が全然いいなと思いました。

プラグインを有効にするには、フロントコントローラに登録する必要があります。
index.phpで以下の行が必要です。

require_once('Custom/Controller/Plugin/DbAdapter.php');

$front = Zend_Controller_Front::getInstance();
$front->registorPlugin(new Zend_Controller_Plugin_DbAdapter());

プラグインで、ログイン状態判定もできそうだなと思っています。