sugyan | 複数のチェックボックスからの入力を受けてDBに格納する場合って普通どういう風にするものなんだろう?各チェックボックスに対応するTINYINTカラムを作る?項目ごとにビット割り当ててINTでまとめる?MYSQLだとSETというのがあるみたいだけど | 2010/03/03(水) 16:41:11 |
conceal_rs | @sugyan @kwappa 「ケースバイケース」ですねw「これで全方向万全な方法!」と言うのはないですから | 2010/03/03(水) 17:27:15 | |
sugyan | @kwappa @conceal_rs やっぱり結論は「ケースバイケース」ですかねw | 2010/03/03(水) 17:16:23 | |
kwappa | @conceal_rs @sugyan 管理さえちゃんとできればそっちの方がお得なんですけどねー。あ、あとは検索対象になると面倒なのかw | 2010/03/03(水) 17:09:54 | |
conceal_rs | @kwappa @sugyan 複数個の場合はカラムは int でビット演算という手法をよく使ってました.限界はありますがw | 2010/03/03(水) 17:07:14 | |
sugyan | @m_norii そうですねー。全部含めて1つのテーブルにしようとするとだいぶ苦しいかな、とは思いました。ありがとうございます! | 2010/03/03(水) 17:04:45 | |
m_norii | @sugyan 個人的好みはTINYINT複数カラムですかねぇ。チェックボックスの数が多いなら、列に展開するのではなく、そこだけ別テーブルにして行に展開するとか。 | 2010/03/03(水) 17:03:11 | |
sugyan | @kwappa ありがとうございます。 やっぱり普通に考えるとそうですかねー。MySQLのSET型はそれなりに便利そうですがやっぱりDB特有のものを使うことになるのもアレですし… | 2010/03/03(水) 17:02:15 | |
kwappa | @sugyan そうですね、tinyint(1)にマップされる、とドキュメントにあります。よっぽどケチらなきゃいけない理由がなければ、チェックボックス1つにつき1カラム用意した方が無難だと思うですよ。識者の反論を待つw | 2010/03/03(水) 16:58:22 |
とりあえず
MySQL :: MySQL 5.6 リファレンスマニュアル :: 11.4.5 SET 型
MySQLには(他は知らない)"SET"というデータタイプがあるらしい。これをDBICで使おうとするとどんなカンジになるのか試してみた。
まずは下記のようにtableを作成。
CREATE TABLE `fuga` ( `id` int(11) NOT NULL AUTO_INCREMENT, `piyo` set('foo','bar','baz') DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8
これをDBIx::Classで操作するためにSchemaクラスとかなんとかを作成する。
DBIx::Class::Schema::Loaderには"dbicdump"というコマンドが付属していて、これを使って一発でclassファイルを作成してくれるらしい。
$ mkdir hoge $ cd hoge $ dbicdump MyDB::Schema dbi:mysql:hoge root Dumping manual schema for MyDB::Schema to directory . ... Schema dump completed. $ ls -R MyDB ./MyDB: Schema Schema.pm ./MyDB/Schema: Result ./MyDB/Schema/Result: Fuga.pm
MyDB/Schema.pm
package MyDB::Schema; # Created by DBIx::Class::Schema::Loader # DO NOT MODIFY THE FIRST PART OF THIS FILE use strict; use warnings; use base 'DBIx::Class::Schema'; __PACKAGE__->load_namespaces; # Created by DBIx::Class::Schema::Loader v0.05003 @ 2010-03-03 20:26:35 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OBak59WuDE/spb1nXCS8GQ # You can replace this text with custom content, and it will be preserved on regeneration 1;
MyDB/Schema/Fuga.pm
package MyDB::Schema::Result::Fuga; # Created by DBIx::Class::Schema::Loader # DO NOT MODIFY THE FIRST PART OF THIS FILE use strict; use warnings; use base 'DBIx::Class::Core'; =head1 NAME MyDB::Schema::Result::Fuga =cut __PACKAGE__->table("fuga"); =head1 ACCESSORS =head2 id data_type: INT default_value: undef is_auto_increment: 1 is_nullable: 0 size: 11 =head2 piyo data_type: SET default_value: undef extra: HASH(0x1278af4) is_nullable: 1 size: 11 =cut __PACKAGE__->add_columns( "id", { data_type => "INT", default_value => undef, is_auto_increment => 1, is_nullable => 0, size => 11, }, "piyo", { data_type => "SET", default_value => undef, extra => { list => ["foo", "bar", "baz"] }, is_nullable => 1, size => 11, }, ); __PACKAGE__->set_primary_key("id"); # Created by DBIx::Class::Schema::Loader v0.05003 @ 2010-03-03 20:26:35 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uOUY6V9Jb8YGT288iMIVsg # You can replace this text with custom content, and it will be preserved on regeneration 1;
の2つが生成された。便利。
これを使ってperlスクリプトから操作して作成や検索してみる。
#!/usr/bin/perl use strict; use warnings; use MyDB::Schema; use Data::Dumper; my $fuga = MyDB::Schema->connect( 'dbi:mysql:hoge', 'root', '', )->resultset('Fuga'); $fuga->delete; $fuga->create({}); $fuga->create({ piyo => 1 }); # 整数値で指定 $fuga->create({ piyo => 'bar' }); # 文字列で指定 $fuga->create({ piyo => 5 }); # 整数値で複数指定 1(= 1<<0)と4(= 1<<2)の論理和 $fuga->create({ piyo => 'baz,bar,foo' }); # 文字列で複数指定 カンマ区切りでできるらしい print "all:\n"; # 作成されたものを全部出力 for my $obj ($fuga->all) { my %inflated_data = $obj->get_inflated_columns; print Dumper( \%inflated_data ); } print "search bar by 'like':\n"; # LIKE検索で"bar"が含まれているものを探す for my $obj ($fuga->search({ piyo => { like => '%bar%' } })) { my %inflated_data = $obj->get_inflated_columns; print Dumper( \%inflated_data ); } print "search bar by '&':\n"; # 論理積で"bar"が含まれているものを探す for my $obj ($fuga->search({ piyo => { '&' => 2 } })) { my %inflated_data = $obj->get_inflated_columns; print Dumper( \%inflated_data ); } print "search bar by 'FIND_IN_SET':\n"; # FIND_IN_SET関数で"bar"が含まれているものを探す for my $obj ($fuga->search_literal('FIND_IN_SET(?, piyo)', 'bar')) { my %inflated_data = $obj->get_inflated_columns; print Dumper( \%inflated_data ); }
$ perl hoge.pl all: $VAR1 = { 'piyo' => undef, 'id' => '6' }; $VAR1 = { 'piyo' => 'foo', 'id' => '7' }; $VAR1 = { 'piyo' => 'bar', 'id' => '8' }; $VAR1 = { 'piyo' => 'foo,baz', 'id' => '9' }; $VAR1 = { 'piyo' => 'foo,bar,baz', 'id' => '10' }; search bar by 'like': $VAR1 = { 'piyo' => 'bar', 'id' => '8' }; $VAR1 = { 'piyo' => 'foo,bar,baz', 'id' => '10' }; search bar by '&': $VAR1 = { 'piyo' => 'bar', 'id' => '8' }; $VAR1 = { 'piyo' => 'foo,bar,baz', 'id' => '10' }; search bar by 'FIND_IN_SET': $VAR1 = { 'piyo' => 'bar', 'id' => '8' }; $VAR1 = { 'piyo' => 'foo,bar,baz', 'id' => '10' };
ちゃんと作成されて、検索もできているっぽい。DBIC_TRACE=1 で生成されたSQL文は
DELETE FROM fuga: INSERT INTO fuga () VALUES (): INSERT INTO fuga ( piyo) VALUES ( ? ): '1' INSERT INTO fuga ( piyo) VALUES ( ? ): 'bar' INSERT INTO fuga ( piyo) VALUES ( ? ): '5' INSERT INTO fuga ( piyo) VALUES ( ? ): 'baz,bar,foo' SELECT me.id, me.piyo FROM fuga me: SELECT me.id, me.piyo FROM fuga me WHERE ( piyo LIKE ? ): '%bar%' SELECT me.id, me.piyo FROM fuga me WHERE ( piyo & ? ): '2' SELECT me.id, me.piyo FROM fuga me WHERE ( FIND_IN_SET(?, piyo) ): 'bar'
となっていた。
intでビットマスクかけての操作をラップしてくれてるだけ、というカンジ?
わざわざこれだけのためにMySQLしか使えないような仕組みにしようとは思えないなー。