Google Android アプリケーション開発入門(SQLite)

Google Android アプリケーション開発入門8章のSQLiteを使うサンプル

google android

HDBCとHDBC-SQLite3を入れた

21章は短い

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan,John Goerzen,Don Stewart
オライリージャパン / ¥ 3,990 ()
在庫あり。

HDBC-2.1.0だと下のようなエラーがずらずらでるのでgitで入れる。

Database/HDBC/SqlValue.hs:585:9:
    Duplicate instance declarations:
      instance Typeable Day
        -- Defined at Database/HDBC/SqlValue.hs:585:9-20
      instance Typeable Day
        -- Defined in time-1.1.4:Data.Time.Calendar.Days

使ってみる

Prelude> :m Database.HDBC Database.HDBC.Sqlite3
Prelude Database.HDBC Database.HDBC.Sqlite3> conn <- connectSqlite3 "drkcore.db"
Prelude Database.HDBC Database.HDBC.Sqlite3> getTables conn
["entries","entry_tags","roles","tags","user_roles","users"]
Prelude Database.HDBC Database.HDBC.Sqlite3> \
quickQuery' conn "select title from entries where pubdate > '2010-01-01'" []
[[SqlByteString "\229\155\155\229\173\163\230\161\156\227\129\174\232\138\ ...

これをutf8で出力したいのでWIKIも読んどく。

pylonsはモデルだけいじりたいときにはどうすんの?

TurboGearsだとこんな感じでいけるじゃないですか?

pylonsはちょっとよく分からなくて結局

sqlite3 -separator , test.db ".import makeinu.csv test_table"

とやってしまった(なんじゃそりゃ)。

既存のSQLiteデータにSQLAlchemyでアクセスする

多数の化合物のファイルはSMILES形式でsqliteのデータベースにしておくと、あつかいやすいのだけど、たまにさくっとアクセスしてゴニョりたいときにsqliteでSQL文発行してとかめんどくさかったりする。

で、SQLAlchemy使うとpythonの対話環境で使えてそのままpybelとかに持っていけるので便利なんだけど、いちいちマッピングしなくても良いらしい。

create_engineでsqliteのファイルを指定して、reflectをTrueにすればよい。

from sqlalchemy import *

db = create_engine('sqlite:///test.db')
metadata = MetaData(bind=db, reflect=True)
table = metadata.tables['table_name']

このdrkcoreのファイルをつかって、タイトルを抜き出してみる。

>>> execfile("satest.py")
>>> stmt = entries.select()
>>> result = stmt.execute()
>>> for row in result:
...   print row.title
... 
blogを変えてみた
卓次郎商店でつけ麺
かど乃やで黒びしおラーメン

らくちん。

ProductName Essential Sqlalchemy
Rick Copeland
Oreilly & Associates Inc / 2719円 ( 2008-06 )


4章くらいまで読んだ。データの永続化用にsqlalchemy使えるようにしとくと仕事が非常に楽になりそうなので、ちゃんと読む。

DBICで多対多

多対多のサンプルとしてTags: Database schemasの“Toxi” solutionをDBICで書いてみる。

関連を表すテーブルに対しては、1対多の関係を設定しておいて、更にmany_to_manyを追加する

図にしてみたらわかりやすくなった。

manytomany

ちなみに、1対多はこちらを、多対多はこちらを参考にしました。

Kzfm::Schema

use 5.008008;
use strict;
use warnings;
use base 'DBIx::Class::Schema';
our $VERSION = '0.01';

__PACKAGE__->load_classes(qw/Tag Bookmark TagMap/);

1;

Kzfm::Schema::Tag

use strict;
use warnings;
use base 'Kzfm::Schema';

__PACKAGE__->load_components(qw/PK::Auto::SQLite Core/);
__PACKAGE__->table('tag');
__PACKAGE__->add_columns(qw/id name/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(tagmap => 'Kzfm::Schema::TagMap','tag_id');
__PACKAGE__->many_to_many('bookmarks' => 'tagmap','bookmark_id');

1;

Kzfm::Schema::Bookmark

use strict;
use warnings;
use base 'Kzfm::Schema';

__PACKAGE__->load_components(qw/PK::Auto::SQLite Core/);
__PACKAGE__->table('bookmark');
__PACKAGE__->add_columns(qw/id name uri description time_created/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(tagmap => 'Kzfm::Schema::TagMap','bookmark_id');
__PACKAGE__->many_to_many('tags' => 'tagmap','tag_id');

1;

Kzfm::Schema::TagMap

use strict;
use warnings;
use base 'Kzfm::Schema';

__PACKAGE__->load_components(qw/PK::Auto::SQLite Core/);
__PACKAGE__->table('tagmap');
__PACKAGE__->add_columns(qw/id bookmark_id tag_id/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->belongs_to('bookmark_id' => 'Kzfm::Schema::Bookmark');
__PACKAGE__->belongs_to('tag_id' => 'Kzfm::Schema::Tag');

1;

制約の部分だけ、考えてみる。

__PACKAGE__->belongs_to('カラム名(アクセサ?)' => 'スキーマ(pm)');
__PACKAGE__->has_many(テーブル名 => 'スキーマ(pm)','カラム名(アクセサ?)');
__PACKAGE__->many_to_many('任意のキー名' => 'テーブル名','多対多関係のカラム');
  • belongs_toはまぁわかりやすい。
  • many_to_manyは任意にキー名決められるのにhas_manyはキー値にテーブル名指定しないとエラー吐くのが謎。
  • many_to_manyでテーブル名のとこをKzfm::Schema::TagMapってやるのが駄目な理由がよくわからない。

あと、さらにユーザーを追加する場合に、ユーザーとブックマークも多対多の関係になるけど、こっちも正規化したほうがいいのか、むしろやりすぎないほうがむしろいいのか悩む。

追記 06.12.04

ボケボケだったことに気付いた。

DBICを使ってみる

最近はmysqlでデータベースを作ることが多かったんだけど、ちょっとしたもの作るのにmysqlはでかすぎるなあとSQLite+DBICをさわり始めてる。

サンプルはPerl/DBIC - Nekokak's core dumpが、よくまとまっていて非常にわかりやすかったので、これの流れで書いた。が、二箇所ほど悩んだのでメモ。ちなみに、SQLiteのせいかどうかはわかりません。

ちなみに、

  • DBIx::Class 0.07003
  • SQLite version 3.3.5

というバージョン。

last_insert_id

1:多の関係を試す をSQLiteで試す。

まずsqlはこんな感じ

create table user (
    id     integer PRIMARY KEY not null,
    name   varchar(256) not null,
    time   date
);

create table bookmark (
    id      integer NOT NULL PRIMARY KEY,
    user_id integer NOT NULL,
    url     text NOT NULL,
    FOREIGN KEY (user_id) REFERENCES user(id)
);

CREATE INDEX user_id_idx ON bookmark (user_id);

で、データベース作成

sqlite3 kzfm.db < test.sql

Schema,User,Bookmarkモジュールはほとんど同じだがload_componentsにPK::Auto::SQLiteを使う。

use strict;
use warnings;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ PK::Auto::SQLite Core/);
__PACKAGE__->table('user');
__PACKAGE__->add_columns(qw/id name/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(bookmark => 'Kzfm::Schema::Bookmark', 'user_id');
1;

こんな感じ。

で、実行してみると、、、

my $i = $schema->resultset('User')->create({name => 'kzfm'});
$schema->resultset('Bookmark')->create({
    user_id => $i->last_insert_id ,
    url => 'http://test.com/'}
);

last_insert_idがないと言われた。単にidだけでよいのか

might_have

loginテーブルにマッピングさせるモジュール

のとこでUser,Loginお互いにmight_haveを指定すると->idでIDがゲットできなかった。

use strict;
use warnings;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ PK::Auto::SQLite Core/);
__PACKAGE__->table('user');
__PACKAGE__->add_columns(qw/id name/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(bookmark => 'Kzfm::Schema::Bookmark', 'user_id');
#__PACKAGE__->might_have(id => 'Kzfm::Schema::Login');
1;

package Kzfm::Schema::Login;

use strict;
use warnings;
use base 'DBIx::Class';
use DateTime::Format::MySQL;

__PACKAGE__->load_components(qw/PK::Auto::SQLite Core/);
__PACKAGE__->table('login');
__PACKAGE__->add_columns(qw/id login_time/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->might_have(id => 'Kzfm::Schema::User');
__PACKAGE__->inflate_column('login_time', {
    inflate => sub { DateTime::Format::MySQL->parse_datetime(shift); },
    deflate => sub { DateTime::Format::MySQL->format_datetime(shift); },
});

幾つかのパターンを試した結果、参照するほうにmight_haveをつけるらしい。あと、SQLiteの場合日付まわりをどう処理すればいいのかよくわかってない。とりあえずMySQLのフォーマットでも動くような気がするんだけど。

というわけで、時間のあるうち(気力のあるうち?)に、多対多まではいっときたい。

sqliteはtrigger使える

SQLite+DBICで色々やるために、SQLiteまわりをみてたらtrigger使えるという事実を知った。

SQLite が認識できる SQL

そういえば、O/Rマッパーとトリガーを一緒に使うと調子いい例ってあるのかな?

とか思った。

sqliteのバックスペースが効かない

sqliteのlinux用バイナリをFedoraCoreで使うと、コマンドプロンプトはバックスペースが効かず、^Hが表示されてしまい、SQL文の修正ができず面倒だ。

と思ってたらCtrl-BackspaceでOKなことを発見した。

でも、コマンド履歴はCtrl-(矢印 or p)では動かなかった。