5月の連休に入って、連休でなければ出来ない事をしている。髭を伸ばしているのである。最初は休みだから髭なんて剃らなくいいやと、いわゆる不精髭だったのであるが、女房が嫌うので、口の上だけを残してみた。いわゆる口髭というやつである。女房は似合うと言ってくれるのだが、本人はまぬけ顔がよけいまぬけに見えるようで、大いに恥ずかしい。出社前日にスパッと剃る事にしよう。
MySQL本がオライリーから出版されたのを記念してRubyとの融合をやってみる事にします。思えば以前には、perlとの融合をやっていましたので久しぶりのMySQLです。正直、すっかりSQLを忘れてしまってますので、復習を兼ねています。FreeBSDですと、MySQLが既にportsになっていますので簡単にインストールできるはずですが、ソースから頑張って入れてみます。mysql-3.22.32を取ってきました。configure一発なのですが、 --with-charset=ujisを忘れないようにします。非力なマシンだとついでに、--with-low-memoryも付けておきます。でも、memori(swap)をゴージャスに要求されますので、覚悟してください。
苦労の上無事インストールが出来たとしましょう。後は本なりマニュアルを読みながら設定をしていきます。私は、最終的にwebからアクセスしたいので次のようにしました。
管理用DBであるmysqlに接続して、ユーザー(nobody)とそのパスワード(password)を設定しました。(権利は何でも出来るゆるゆる設定です)
[sakae@atom]$ mysql --user=root mysql Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2 to server version: 3.22.32 Type 'help' for help. mysql> GRANT ALL PRIVILEGES ON *.* TO nobody@localhost -> IDENTIFIED by 'password' WITH GRANT OPTION; Query OK, 0 rows affected (0.02 sec) mysql> GRANT ALL PRIVILEGES ON *.* TO nobody@"%" -> IDENTIFIED by 'password' WITH GRANT OPTION; Query OK, 0 rows affected (0.01 sec) mysql> select host,user,password,Create_priv,Grant_priv from user where user='nob ody'; +-----------+--------+------------------+-------------+------------+ | host | user | password | Create_priv | Grant_priv | +-----------+--------+------------------+-------------+------------+ | localhost | nobody | 5d2e19393cc5ef67 | Y | Y | | % | nobody | 5d2e19393cc5ef67 | Y | Y | +-----------+--------+------------------+-------------+------------+ 2 rows in set (0.00 sec)
次に出来立てのユーザーでDBにアクセスしてみます。 [sakae@atom]$ mysql -u nobody mysql ERROR 1045: Access denied for user: 'nobody@localhost' (Using password: NO) おっとパスワードの入力を忘れたため文句を言われました。やり直し [sakae@atom]$ mysql -u nobody -ppassword mysql Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A 今度はうまくいきました。 rubytestという新たなDBを作ります。 mysql> CREATE DATABASE rubytest; Query OK, 1 row affected (0.01 sec) mysql> use rubytest; Database changed mysql> show tables; Empty set (0.01 sec) まだテーブルは有りません。しょうがないので、マニュアルからテーブルの例をぱくってきます。ペットのテーブルです。
mysql> CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20), -> species VARCHAR(20), sex CHAR(1), birth DATE, death DATE); Query OK, 0 rows affected (0.02 sec) mysql> show tables; +--------------------+ | Tables in rubytest | +--------------------+ | pet | +--------------------+ 1 row in set (0.00 sec) mysql> DESCRIBE pet; +---------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+-------+ | name | varchar(20) | YES | | NULL | | | owner | varchar(20) | YES | | NULL | | | species | varchar(20) | YES | | NULL | | | sex | char(1) | YES | | NULL | | | birth | date | YES | | NULL | | | death | date | YES | | NULL | | +---------+-------------+------+-----+---------+-------+ 6 rows in set (0.01 sec)これで入れ物が出来ました。次は中身です。pet.txtというプレーンファイルにデータを用意します。フィールドのセパレータはTABです。内容の無い所は、\N を入れておきます。
----- pet.txt ------------------------------------ name owner species sex birth death Fluffy Harold cat f 1993-02-04 : ブ− 佐藤 pig f 1993-11-14 デメ 森 fish \N 2000-02-25 ------------------------------------------------ mysql> LOAD DATA LOCAL INFILE "pet.txt" INTO TABLE pet; Query OK, 13 rows affected (0.01 sec) Records: 13 Deleted: 0 Skipped: 0 Warnings: 13 mysql> select * from pet; +----------+--------+---------+------+------------+------------+ | name | owner | species | sex | birth | death | +----------+--------+---------+------+------------+------------+ | name | owner | species | s | 0000-00-00 | 0000-00-00 | | Fluffy | Harold | cat | f | 1993-02-04 | NULL | | Claws | Gwen | cat | m | 1994-03-17 | NULL | | Buffy | Harold | dog | f | 1989-05-13 | NULL | | Fang | Benny | dog | m | 1990-08-27 | NULL | | Bowser | Diane | dog | m | 1998-08-31 | 1995-07-29 | | Chirpy | Gwen | bird | f | 1998-09-11 | NULL | | Slim | Benny | snake | m | 1996-04-29 | NULL | | 黒 | 小林 | cat | m | 1988-04-01 | 1992-10-14 | | ピ− | 佐々木 | bard | f | 1997-06-23 | NULL | | トラ | 山口 | cat | f | 1998-08-13 | NULL | | ブ− | 佐藤 | pig | f | 1993-11-14 | NULL | | デメ | 森 | fish | NULL | 2000-02-25 | NULL | +----------+--------+---------+------+------------+------------+ 13 rows in set (0.01 sec)ちゃんと読み込めているようですが、タイトルまで登録されちゃいました。消しておきましょう。
mysql> delete from pet where name='name'; Query OK, 1 row affected (0.00 sec) mysql> select * from pet order by birth; +----------+--------+---------+------+------------+------------+ | name | owner | species | sex | birth | death | +----------+--------+---------+------+------------+------------+ | 黒 | 小林 | cat | m | 1988-04-01 | 1992-10-14 | | Buffy | Harold | dog | f | 1989-05-13 | NULL | | Fang | Benny | dog | m | 1990-08-27 | NULL | | Fluffy | Harold | cat | f | 1993-02-04 | NULL | | ブ− | 佐藤 | pig | f | 1993-11-14 | NULL | | Claws | Gwen | cat | m | 1994-03-17 | NULL | | Slim | Benny | snake | m | 1996-04-29 | NULL | | ピ− | 佐々木 | bard | f | 1997-06-23 | NULL | | トラ | 山口 | cat | f | 1998-08-13 | NULL | | Bowser | Diane | dog | m | 1998-08-31 | 1995-07-29 | | Chirpy | Gwen | bird | f | 1998-09-11 | NULL | | デメ | 森 | fish | NULL | 2000-02-25 | NULL | +----------+--------+---------+------+------------+------------+ 12 rows in set (0.01 sec)ふう、長かったですがDBの準備が整いました。
mysql-ruby-2.2.0をRAAから頂いてきました。インストールは簡単なので省略します。まずは、ちゃんと動くか確認。
--- checkdb.rb --------------------------------------------- #!/usr/local/bin/ruby require "mysql" m = Mysql.new('localhost', 'nobody', 'password', 'rubytest') res = m.query("select * from pet") fields = res.fetch_fields.filter do |f| f.name end puts fields.join("\t") res.each do |row| puts row.join("\t") end m.close ------------------------------------------------------------ [sakae@atom]$ ./checkdb.rb name owner species sex birth death Fluffy Harold cat f 1993-02-04 Claws Gwen cat m 1994-03-17 Buffy Harold dog f 1989-05-13 Fang Benny dog m 1990-08-27 Bowser Diane dog m 1998-08-31 1995-07-29 Chirpy Gwen bird f 1998-09-11 Slim Benny snake m 1996-04-29 黒 小林 cat m 1988-04-01 1992-10-14 ピ− 佐々木 bard f 1997-06-23 トラ 山口 cat f 1998-08-13 ブ− 佐藤 pig f 1993-11-14 デメ 森 fish 2000-02-25どうやら大丈夫そうです。次は、Webからです。本当は、cgiで検索条件等を入力出来るようにするのが筋なのでしょうが手抜きして、アクセス出来るかどうかのチェックをします。
--- sample.rb ---------------------------------------------- #!/usr/local/bin/ruby require "mysql" require "cgi-lib.rb" def test_db out = "" m = Mysql.new('localhost', 'nobody', 'password', 'rubytest') res = m.query("select * from pet") fields = res.fetch_fields.filter do |f| f.name end out << fields.join("\t") << "\n" res.each do |row| out << row.join("\t") << "\n" end m.close return out end CGI::print{ CGI::tag("HTML"){ CGI::tag("HEAD"){ CGI::tag("TITLE"){"Test MySQL"} } + CGI::tag("BODY"){ CGI::tag("H2"){"MySQL Access Test from Web"} + CGI::tag("HR") + CGI::tag("PRE"){ test_db } + CGI::tag("HR") } } } ------------------------------------------------------------ [sakae@atom]$ w3m -dump http://atom/cgi-bin/sample.rb MySQL Access Test from Web ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ name owner species sex birth death Fluffy Harold cat f 1993-02-04 Claws Gwen cat m 1994-03-17 Buffy Harold dog f 1989-05-13 Fang Benny dog m 1990-08-27 Bowser Diane dog m 1998-08-31 1995-07-29 Chirpy Gwen bird f 1998-09-11 Slim Benny snake m 1996-04-29 黒 小林 cat m 1988-04-01 1992-10-14 ピ− 佐々木 bard f 1997-06-23 トラ 山口 cat f 1998-08-13 ブ− 佐藤 pig f 1993-11-14 デメ 森 fish 2000-02-25 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ちゃんとアクセスしてくれています。気になるスピードですが [sakae@atom]$ /www/bin/ab -n 500 http://atom/cgi-bin/sample.rb This is ApacheBench, Version 1.3c <$Revision: 1.38 $> apache-1.3 Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/ Server Software: Apache/1.3.12 Server Hostname: atom Server Port: 80 Document Path: /cgi-bin/sample.rb Document Length: 526 bytes Concurrency Level: 1 Time taken for tests: 30.544 seconds Complete requests: 500 Failed requests: 0 Total transferred: 328000 bytes HTML transferred: 263000 bytes Requests per second: 16.37 Transfer rate: 10.74 kb/s received Connnection Times (ms) min avg max Connect: 0 0 12 Processing: 51 60 897 Total: 51 60 909 [sakae@atom]$本当はmod_rubyでも使えば格段にスループットが上がると思うのですが、
[Wed May 4 21:27:49 2000] [error] ruby script error /www/cgi-bin/sample.rb:2:in `require': /usr/local/lib/ruby/1.4/i386-freebsd4.0/mysql.so: Undefined symbol "rb_eTypeError" - /usr/local/lib/ruby/1.4/i386-freebsd4.0/mysql.so (LoadError) from /www/cgi-bin/sample.rb:2エラーになってしまい、使えませんでした。グスン。多分mod_rubyが使えれば3秒ぐらいで回るのではないかと思います。
MySQL本が出たと言うので東京まで出かけたのですが、結局その本は買わずにカーネル本等を仕入れてしまいました。こりゃいかんと言う訳で、オープンデザイン(No.38)を地元で買っちゃいました。rubyの連載とperlによるWebプログラミングなんて記事が出ていたからです。この記事では、Postgress+DBI,DBDで解説されてましたので、rubyでやったらどうなるのとヘソを曲げてみたのでした。記事の中にはバッチスクリプトを書いて、テキストデータをDBに入れる紹介がありましたので、真似てみました。どうせ作るなら実用になりそうなものを(あくまで、私にとってですが)書いてみようという事で、次のような状況を想定してます。
大型ホストから出て来るDBのデータは、固定長のファイルになっている。これをMySQLに移そうという訳です。固定長でフィールドが分割(?)されてますので、そのフィールド名や、長さ、MySQLでのフィールドのタイプを定義したスペックファイルを用意して、それを元にデータを移す事にします。
--- spec file ----------------------- e 8 VARCHAR(8) k 12 VARCHAR(12) g 10 VARCHAR(10) u 17 VARCHAR(17) d 8 INTEGER -----------------------------------左からフィールド名、長さ、MySQLのタイプをTAB区切りで書いたファイルです。フィールド名と長さはそのまま大型ホストのDB仕様書から貰ってくればいいでしょう。(フィールド名は実名にすると差障りがあるのでイニシャル文字にしてます) スクリプトは次のようなものです。一時ファイルを作って一気読みでMySQLへ持って行きます。
--- batch.rb -------------------------------------------------------- #!/usr/local/bin/ruby require "mysql" $SPEC_FILE="spec" # table spec file $DAT_FILE="dat.txt" # fixed length data file $TABLE_NAME="smpl" # table name $m = Mysql.new('localhost', 'nobody', 'password', 'rubytest') def mk_format_data fmt = "" t_spec = Array.new() File.foreach( $SPEC_FILE ){ |ll| ll.chomp! field,len,type=ll.split( "\t" ) fmt += "A#{len}" # for unpack template t_spec << "#{field} #{type}" # for create table } return fmt, t_spec.join(",\n") end def del_table $m.query( "DROP TABLE #{$TABLE_NAME} \n" ) end def mk_table(tbl_spec) $m.query( "CREATE TABLE #{$TABLE_NAME} ( #{tbl_spec} )\n" ) end def ins_data(fmt) f=File.open( "#{$$}", "w" ) # temp file (tsv format) File.foreach( $DAT_FILE ){ |l| l.chomp! d=l.unpack( "#{fmt}" ) f.puts d.join( "\t" ) } f.close $m.query( %Q!LOAD DATA LOCAL INFILE "#{$$}" INTO TABLE #{$TABLE_NAME}\n! ) File.unlink( "#{$$}" ) end fmt,tbl_spec = mk_format_data() ## del_table() ##### if you need mk_table(tbl_spec) ins_data(fmt) $m.close --------------------------------------------------------------------------- [sakae@atom]$ wc dat.txt 782 42029 232579 2353624 dat.txt 42029 238167 1740310 782上記は、一時ファイルを消さないようにしてwcしてみたものです。4万行以上あるデータですが
[sakae@atom]$ time ./batch.rb real 0m3.086s user 0m2.394s sys 0m0.149sかなりのスピードで処理が終っています。本当にデータが入ったのでしょうか? ちょっと調べてみます。
mysql> DESCRIBE smpl; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | e | varchar(8) | YES | | NULL | | | k | varchar(12) | YES | | NULL | | | g | varchar(10) | YES | | NULL | | | u | varchar(17) | YES | | NULL | | | d | int(11) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 5 rows in set (0.01 sec) mysql> select count(e) from smpl; +----------+ | count(e) | +----------+ | 42029 | +----------+ 1 row in set (0.23 sec)大丈夫そうですね。所で、ruby-mysqlにはDBIがサポートしているプレースホルダの機能は無いのでしょうか? 何でもqueryに突っ込んじゃえばいいので、まどろっこしい事はするなと言う事なのでしょう。深く考えない事にします。