index の種類、複合 indexB!

はじめに

昨日は、 Data::Model を install して、簡単なテーブル操作を行うところまでやりました。

ちなみに、本トラックで書いているサンプルを github に上げています。

http://github.com/yappo/document-datamodel-advent-calendar-2009-examples

最終日にはなんかしら動くものが出来るかなと思う今日この頃です。

本日は、 primary key をはじめとした index の tips のご紹介です。

unique index

普通の RDBMS には UNIQUE をカラムに指定できますよね。もちろん Data::Model でだって制約できます!

user テーブルの nickname なんてのは重複しちゃいやだと思うので、 unique 制約かけましょう。

install_model user => schema {
    key 'id';
    unique 'nickname'; #ここ
    columns qw/ id nickname /;
};

以下のコードで、ちゃんと ERROR! が出力されるハズです。

    # id = 1, nickname = 'Yappo' で user テーブルにレコードを作成
    $bookmark->set(
        user => 1 => {
            nickname => 'Yappo',
        },
    );

    # id = 2, nickname = 'Yappo' で user テーブルにレコードを作成しようとする
    eval {
        $bookmark->set(
            user => 2 => {
                nickname => 'Yappo',
            },
        );
    };
    print "ERROR!\n" if $@;

ふつうの index

さて、ここまでは primary key やら unique 制約のついたものでした。

普通に index を使うのも unique と同じ感じでできます。

install_model bookmark => schema {
    key [qw/ url_id user_id /];
    index 'user_id'; # ここ
    columns qw/ url_id user_id /;
};

index で検索

unique/index を活用してレコードを取得するには lookup の代わりに get を使います。

    # index を使って検索
    # SELECT * FROM user WHERE user_id = 2; してレコードを取得
    my $itr = $bookmark->get(
        bookmark => {
            index => { user_id => 2 }
        }
    );
    $row = $itr->next; # 最初の1レコード目を取得
    print sprintf "URL_ID: %d\nUSER_ID: %s\n", $row->url_id, $row->user_id;

    # SELECT * FROM user WHERE nickname = 'Yappo'; してレコードを一件取得
    my $itr = $bookmark->get(
        user => {
            index => { nickname => 'Yappo' }
        }
    );
    my $row = $itr->next; # 最初の1レコード目を取得
    print sprintf "ID: %d\nNICKNAME: %s\n", $row->id, $row->nickname;

unique と index の違いは、 unique 制約の有る無しだけなので、検索する時の使い方は違いありません。

複合 primary key

昨日は user テーブルの id というカラムに primary key を張りました。

install_model user => schema {
    key 'id';
    columns qw/ id nickname /;
};

実はこれ、 key に対して ARRAY リファレンスを渡すだけで、複合 index の設定ができるんです。

試しに bookmark テーブルの primary key を複合 index にしてみましょう。

install_model bookmark => schema {
    key [qw/ url_id user_id /];
    columns qw/ url_id user_id /;
};

たった、これだけです。

で、実際に使うわけですが、昨日は Data::Model は KVS 指向の API を提供していると言いました。

これの考えで使い方を考えると、 key の定義が ARRAY リファレンスなんだから、 ARRAY リファレンスを key にして使えば良い。となります。

実際、以下のような使い方です。

my $bookmark = MyBookmark->new;

# url_id = 1, user_id = 2 で bookmark テーブルにレコードを作成
$bookmark->set(
    bookmark => [1, 2]
);

# SELECT * FROM bookmark WHERE url_id = 1 AND user_id = 2; してレコードを一件取得
my $row = $bookmark->lookup( bookmark => [1, 2] );
print sprintf "URL_ID: %d\nUSER_ID: %s\n", $row->url_id, $row->user_id;

# 取得したレコードを DELETE 文で削除
$row->delete;

実行結果は下記の通り

$ perl 02.pl
URL_ID: 1
USER_ID: 2

key の定義したカラムの順番に値を指定すれば良いことが分かります。

key だけで済む場合はちょっと違う

ちなみに、昨日の user テーブルへの値の保存方法が

$bookmark->set(
    user => 1 => {
        nickname => 'Yappo',
    },
);

だったのに対して

$bookmark->set(
    bookmark => [1, 2]
);

と、 HASH リファレンスがなくなっています。

なぜかというと、 bookmark の定義を思い出して欲しいんですけど定義されてるカラム全てに対して primary key の index が張られているので、わざわざカラムに対する値を設定する必要がないのです。

複合 index/unique

もちろん index/unique にも複合 index を設定できます。

ただし index name を指定しなければいけませんので注意してください。

install_model foo => schema {
    unique index_name1 => [qw/ a b /];
    index index_name2 => [qw/ c d /];
    columns qw/ a b c d /;
};

これはそれぞれ以下のように検索出来ます。

    # unique 制約の index (index_name1) で検索
    # SELECT * FROM foo WHERE a = 'a_data' AND b = 'b_data';
    my $itr = $bookmark->get(
    foo => {
        index => { index_name1 => [qw/ a_data b_data /] },
    );

    # ふつうの index (index_name2) で検索
    # SELECT * FROM foo WHERE c = 'c_data' AND d = 'd_data';
    my $itr = $bookmark->get(
    foo => {
        index => { index_name2 => [qw/ c_data d_data /] },
    );

まとめ

今日は index の設定に焦点をあてました。

明日は、チュートリアル的なモノを skip して tips を書きたいです!