ham-capのブログ

プログラミング学習の記録

【PostgreSQL・Ruby】異界と現世を繋ぐゾ!(RubyのコードからPGを使ってPostgreSQLにアクセスします)

もくじ

したいこと

Rubyで書いたプログラムからデータベース(以下、「DB」という)にアクセスしたい。

とりあえず今回はPostgreSQLで作成したDBからPGというgemを使って全てのレコードを取り出してみることにします。

PGのインストール、DB・テーブルの作成等は済んでいて、コードさえ正しく書けばDBを使える状態が前提になります。

DBとプログラムは別の世界

以前どこかで、DBというのはプログラムから見ると全く別の概念で構成された世界、異界であるときいたことがあります。

言われてみると確かにプログラムはコンピュータに話しかけるための言語(RubyJavaScript等)によって表現された世界ですが、DBに話しかけるためにはSQLという専用の言語を使わなくてはならず、コミュニケーションの方法からして全然違うわけです。

僕の場合、普段はRubyの世界でオブジェクトが~とかメソッドが~とか言って遊んでいるわけですが、DBの世界でオブジェクトだのメソッドだの言っても向こうでは「???」となってしまいます。そもそもそんな概念は向こうの世界にはありません。

そりゃそうです。 宇宙人に日本語で戦車道を嗜む女子高生の話をして通じるわけがないのと一緒です。

あくまで僕個人のイメージですが、Rubyの世界が僕たちの住んでいる「モノ」が存在する世界だとすると、DBの世界はふわふわした雲みたいなものしかない世界、という感じです。

ということは、DBからプログラムにデータをもってきていじくりまわすためには、(あくまでもイメージですが)そんなふわふわの世界に存在する雲のようなものを僕たちが住んでいる世界の「モノ」に変換して、手で触れられる存在にする必要があります。味もみておこう。

で、そんなすんばらしいことを実現してくれるのが本日の主役PGパイセンです。

PG

Rubyのコード内でSQLを発行してDBにアクセスするためのgemです。

deveiate.org

手順としては、

  1. DBとのコネクションを構築
  2. SQLを発行
  3. レコードをゲット!

という感じです。

順番にやってみます。

手順

1. DBとのコネクションを構築

これができれば勝ったようなもんです。

いくつか方法はあるようですが、今回はPG::connectionクラスに接続先のDBの情報を渡してインスタンスを作成するパターンでやってみます。

require 'pg'

#DBの情報を変数へ格納
host = '{IPアドレス}'
port = {ポート番号}
db = '{DB名}'
user = '{ログインユーザー名}'
password = '{パスワード}'

#上で定義した変数を`PG::Connection.new`の引数として渡してインスタンスを作成
connection = PG::Connection.new(host: host, port: port, dbname: db, user: user, password: password)

変数connectionがDBとのコネクションということになります。

なんだかイメージが湧きにくいのですが、PG::Connectionクラスのインスタンスワームホールみたいなものだと思っています。

余談ですが、ワームホールといえば個人的には映画『インターステラー』に登場したワームホールが印象に残っています。

普通ワームホールというと「地面に空いた穴」みたいなものや、ドラえもんに登場する通りぬけフープのような平面的なものを想像しがちなんですが、『インターステラー』のワームホールはなんと「球体状の穴」という摩訶不思議なものでした。要は360度どこから見ても穴なので、どこからでもその中に入っていけるという脳がパニックになりそうな代物です。

ちなみに、この記事の本筋とは関係ないですが『インターステラー』は傑作だと思うのでご興味があれば是非ご覧になってみてください。「ウラシマ効果」や「事象の地平面」あたりのワードに反応できる人は間違いなく楽しめます。

話がだいぶそれましたが、僕の頭の中では異界であるDBにつながっているPG::Connectionクラスのインスタンスはまさに「球体状の穴」のイメージがぴったりで、このコードを書きながら楽しんでいました。

だってただの穴より立体的な穴のほうがオブジェクトっぽいじゃないですか(?)

というわけで、異界につながる穴ができました。

あとはここに何かを投げ入れれば向こうの世界に情報が登録され、正しい呼び方をすれば向こうの世界にある情報がこちらの世界で使えるオブジェクトになって穴から出てくるわけです。

2. SQLを発行 → 3. レコードをゲット!

さて今回はDB内の全ての情報をまるっと取ってきたいと思います。 やり方は簡単で、先ほど用意した変数connectionexecメソッドを使ってSQLを投げてやればおkです。

all_data = connection.exec("SELECT * FROM テーブル名")

これでテーブル名で指定したテーブル内の全てのレコードの全てのカラムから情報を引っ張り出すことができました。 もう少し詳しく説明すると、connection.execの返り値はPG::Resultクラスのインスタンスです。

構造としてはハッシュを要素として持つ配列です。

ハッシュ1つで1レコード、各カラム名と値がそれぞれkeyとvalueになります。

例えばDBが以下のような構造の場合、

カラム1 カラム2 カラム3
1 foo hoge
2 bar fuga
3 buzz puni

返り値は

[ { カラム1 => 1, カラム2 => foo, カラム3 => hoge }, { カラム1 => 2, カラム2 => bar, カラム3 => fuga }, { カラム1 => 3, カラム2 => buzz, カラム3 => puni } ]

という形になります。

この返り値について一点気を付けなければいけないのは、実態としては配列であっても、あくまでもPG::Resultクラスのインスタンスだということです。

なので、Arrayクラスの全ての機能が使えると思っていると変なところで詰まるかもしれません。(未確認)

が、ご安心ください。

PG::ResultにはEnumerableモジュールがincludeされているため、to_aメソッドが使えます。

そのままでは扱いにくいという場合はto_aArrayクラスにしてしまいましょう。

まとめ

というわけで、これで異界と現世をつないでモノをやり取りすることに成功しました。

あとは煮るなり焼くなりお好きにどうぞ。

どの記事でもそうなのですが、例え話などはあくまでも僕のイメージや感覚的なものを文章にしているので、僕と感性が合わない人にとってはあまりピンとこない部分もあるかと思います。

そのあたりは各自の脳内で読み替えたり補完したりしてください。

では、今回はこんな感じで。

記載内容に間違い等があった場合にはこっそり教えていただければ幸いです。

最後までお読みいただきありがとうございました。