雑草SEの備忘録

東大を卒業し、SEとして働くことになった。備忘録的に作業を綴る。

RailsリポジトリをTravisCI, CodeCov, CodeClimateと連携

Ruby on Railsエンジニア2年目で初めて本格的にRails newをして諸々の初期設定をしたので備忘録的に作業を綴ります。

CIツールはCircleCIなどいくつかありますが、今回はTravisCIを利用させていただきました。

TravisCIの設定

language: ruby

rvm:
 - 2.3.1
services:
 - postgresql
bundler_args: '--without development --deployment'
cache: bundler
before_script:
  - cp config/database.travis.yml config/database.yml
  - bundle exec rake db:create
  - bundle exec rake db:migrate
script:
  - bundle exec rspec spec
  • config/database.travis.ymlを追加します。↑のbefore_scriptのところでcpしているファイルですね。
test:
  adapter: postgresql
  database: travis_ci_test
  username: postgres
  • Travis CIのバッチをREADME.mdに追加したい場合は追加してください。
[![Build Status](https://travis-ci.org/[your_account]/[your_repository].svg?branch=master)](https://travis-ci.org/[your_account]/[your_repository])

CodeCovの設定

gem install travis

travisのgemをインストールします。Gemfileに書いてbundle管理下にしても良いかと思います。

group :development do
   gem 'travis'
end
  • spec/spec_helper.rbを修正。(rspecを使っている場合)
require 'simplecov'

SimpleCov.start 'rails'

if ENV['CODECOV_TOKEN']
  require 'codecov'
  SimpleCov.formatter =  SimpleCov::Formatter::Codecov
end

RSpec.configure do |config|
  (省略)
  • coverageディレクトリができるので、git管理から外しておくと良いでしょう。
/coverage
  • CodeCovに行ってGitHubでSignUpし、自分のリポジトリを追加します。
    • Repository Upload Tokenが出てくるのでコピーしておきます。
  • travisコマンドでCODECOV_TOKENを暗号化します。
    • Gemfileに追加した場合、bundle execが必要な場合あります。
travis encrypt CODECOV_TOKEN=[Repository Upload Token]
  • そうすると次のような意味不明な文字の羅列が出てくるのでそれをコピーし、
secure: "zaZ/DaFV・・・"
  • .travis.ymlに追加します。(本当はもっと長い。ターミナルの横幅にもよるが、100文字ぐらいで6行ほど)
env:
  global:
    - secure: "zaZ/DaFV・・・"
  • これでGitHubにpushしてテストが終わると自動的にCodeCovに情報が通知され、カバレッジが見れるようになります。
  • バッジをREADEME.mdにつけたい方は、以下のようなコードを埋め込んでください。CodeCovの画面上からも取得できます。
[![codecov.io](https://codecov.io/github/[your_account]/[your_repository]/coverage.svg?branch=master)](https://codecov.io/github/[your_account]/[your_repository]?branch=master)

CodeClimateとの連携

  • カバレッジの取得だけでよければCodeCovでいいかと思います。逆に、カバレッジ以外のメトリクスも欲しければCodeClimateを利用して、CodeCovは必要ないでしょう。今回は欲張り的に2つ入れてみたいと思います。
  • カバレッジを取得しないのであれば、CodeClimateのサイトに行きGitHubでSignUpし、自分のリポジトリを追加すれば良いです。
  • バッチをREADME.mdに追加したい場合は、以下のような感じで。
[![Code Climate](https://codeclimate.com/github/[your_account]/[your_repository]/badges/gpa.svg)](https://codeclimate.com/github/[your_account]/[your_repository])
  • カバレッジを取得したい場合は、CodeClimateの右下にあるTest Coverageのところからトークンを取得し、CodeCovと同じように暗号化します。
travis encrypt CODECLIMATE_REPO_TOKEN=[token]
  • .travis.ymlに追加する。
env:
  global:
    - secure: '・・・' # CodeCov用
    - secure: '・・・' # CodeClimate用
  • .travis.ymlにCodeClimateに送信するコマンドを追加します。
after_success:
  - bundle exec codeclimate-test-reporter

以上、一通り作業を終えてから振り返り的にメモをしているので抜け漏れがあるかもしれませんがその際はご指摘お願いします。

rubyで再帰

先日、関数型の初心者の勉強会に参加してきました。
私は、もともと情報系の学科を卒業したわけではないので、アルゴリズムや関数型の考え方は全くの初心者です。

なので、再帰の考え方に慣れるのにとても大変でした。まだ慣れているとは言えないレベルですが。。
備忘録もかねて、rubyで実装してみたいと思います。

今回はmapを実装してみたいと思います。rubyのmap関数はよく使うと思いますが、
配列の各要素に対してある処理を施し同じく配列を返す関数です。

こんな感じですね。

["1", "2", "3"].map { |num| num.to_i }
["1", "2", "3"].map(&:to_i)

各要素をFixnum型にしています。

今回のmapは引数は二つ。
一つ目は処理の対象となる配列。二つ目は処理を施したい関数です。
rubyの場合、関数をオブジェクトにするのはprocとかlambdaですので、これを利用します。

※proc、lambdaについてはこのあたりの記事がわかりやすいです。
http://qiita.com/kidach1/items/15cfee9ec66804c3afd2
Rubyの ブロック、Proc.new、lambdaの違い - Qiita

ではさっそく、こんな感じで実装してみました。

def map(array, f)
  return [] if array.empty?
  [f.call(array[0])] + map(array[1..-1], f) 
end
# もちろんarray.firstやarray.drop(0)を利用してもOK。

1個目の要素に処理をして残りの配列をもう一度自分自身に引き渡す。
実際に手で処理を追っていくと理解が深まりそうです。
だんだんやっていくと空配列になるので、そのときは処理は終わるようにしています。

実際に使ってみた例はこんな感じです。

[1] pry(main)> def map(array, f)
[1] pry(main)*   return [] if array.empty?
[1] pry(main)*   [f.call(array[0])] + map(array[1..-1], f)
[1] pry(main)* end
=> :map
[2] pry(main)> twice = ->(x) { x * 2 }
=> #<Proc:0x007fc3c2c82700@(pry):5 (lambda)>
[3] pry(main)> map([1,2,3,4,5], twice)
=> [2, 4, 6, 8, 10]

ただし、このような再帰関数でうと、関数呼び出しの階数が深くなるとスタックオーバーフローが発生してしまいます。
そこで使うのが末尾再帰というものだそうです。

実装してみた結果がこんな感じです。

def map(array, f, result = [])
  return result if array.empty?
  map(array[1..-1], f, result + [f.call(array[0])])
end

今度はどんどんresultに新しい配列が入っていくので空配列になったときはresultを返すようにします。

配列の掛け算や足し算をする処理なんかも再帰を使って書くとすっきりします。

def sum(array)
  return 0 if array.empty?
  array[0] + sum(array[1..-1])
end

def product(array)
  return 1 if array.empty?
  array[0] * product(array[1..-1])
end

左畳み込みと右畳み込みがあり、今回は、配列の左側から処理をしていっているので、左畳み込みで処理をしてみました。

「オブジェクト指向設計実践ガイド」を読んだ感想

9月2日に発売になったSandi Metz著の「Practical Object-Oriented Design in Ruby: An Agile Primer (Addison-Wesley Professional Ruby) 」の邦訳版です。

私はプログラムの世界には新卒で入社したSIerからJavaから学んだので、新卒の研修ではオブジェクト指向については基本から学びましたが、実戦経験ではRubyがほとんどです。

ただ、これまでプログラムを書いてきてもオブジェクト指向についてなんとなくこんなもんかなーと思いながら先輩エンジニア方のアドバイスもいただきながら書いてきました。

ここらで今一度整理してオブジェクト指向で実装するにはどうしたら良いかを学んでみたいと思い、本書を購入しました。(正確には会社で買ってもらったので、読み終わったら会社の本棚に行きます)

 

この本の英語版自体は、ドイツ人が持っていたので、ちらっと読んだことがあります。それは以前の記事でも少し紹介しました。本書を読む目的としてはドイツ人とのコミュニケーションを円滑に進めるためというのも大きいです。

この本の感想としては発売前に書いてくれている方がいたので、概ね同意します。

d.hatena.ne.jp

とくに注意書きの*3については、私も以前の記事で書いた通り違和感を覚えており、このブログの方に共感をいたしました。

 

さて、私独自の感想については以下のような感じです。

よかった点
  • ダッグタイピングや継承のまわりは勉強になりました。特に継承する際、initializeをオーバーライドしてsuperを使ったりせずにpost_initializeメソッドなどを作ってそれをオーバーライドするというのは考えたことのなかった発想でした。
  • コンポジションのところでOpenStruct使えば良いんじゃない?と思って読み進めたらイメージ通りに実装していて、自分の考え方はあながち間違っていなかったと自信が少しつきました。
悪かった点
  • 邦訳が悪いのか、それともそもそもの英語が悪いのかわからないのですが、文章が少し冗長に感じました。コードで説明してくれているので、勘の良い人はコードのbefore、afterを見るだけで理解出来ると思います。
  • リファクタリング:Rubyエディション を読んだときは心揺さぶられたのですが、そこまでのものではありませんでした。一般的なオブジェクト志向の知識を持っていて、Effective Rubyリファクタリング:Rubyエディション を読んだことのある人はさらっと読む程度で良いのではないかと思います。

全体的にはAmazon的レビューで星3つ ぐらいでしょうか。

楽々ERDレッスン

前々回の記事で、リレーショナルデータベースに関する良書として、こちらの本の紹介をしました。

今回は、前々回も紹介した、楽々ERDレッスンも読み終わったので紹介したいと思います。

この本は大きく3つの部に分かれています。第1部のDB設計総論、第2部のRDBMS総論、第3部の楽々ERDレッスンです。

 

第1部のDB設計総論では、データベース設計の手順について書かれているのですが、その中で口を酸っぱくして書かれているのは、アイデンティファイアをつけるべきだということです。ここでいうアイデンティファイアとは、いわゆるid。Railsを触ったことのある方なら、テーブルをつけると問答無用で各タプルにidが付与されます。これがこの本でいうアイデンティファイアであると言えます。

純粋なテーブル設計をしていた人がRailsを触ると、何勝手にid振ってくれてんねん!と思う人もいると思います。かくいう私がそうでした。私はRailsをやるまえにデータベーススペシャリストの資格を取得していたので、複合キーなども駆使して他テーブルと連関づけるというのを普通に(社会人2年目の4月に資格をとったので、実務上の経験ではなく、試験の問題上でですが)やっていました。そういう人から見ると全てのデータにidを付与するというのは奇妙に感じます。しかし、楽々ERDレッスンでは、idを付与することがいかに有用かが書かれています。詳細は本を読んでいただくとして(きちんと説明しきる自信もない)、実はこれは私もRailsを始めて1年近く経ちましたが、実感として感じていることです。idの付与があることで他テーブルとの関連づけが非常に楽になりました。

 

第2部は経営資産としてのRDBMSの重要性について書かれています。データはもはや経営にとって欠かせないものになっています。これをいかに管理し、いかに活用するかが競争力のある会社になるかどうかの肝となってくるのは言うまでもありません。経営資産であるデータをうまく管理すること、そのためには、ERDを作ってデータを正規化(筆者は数学的正規化と業務的正規化の両方を考える必要があると言っています)する必要があり、その過程で既存の業務の見直しもできることになります。

確かに、RDBMSにデータを落とし込めるということは、既存の業務を正しく理解していないといけません。ERDを書いてデータを管理するということは、業務理解が大前提です。これは現実の世界と向き合うということなので、ただ機械と向き合えば良いプログラミングの世界とは違ったスキルが求められます。個人的には、ソフトウェアエンジニアとしてこの部分こそが一番面白みのあるところなのではないかと思っています。

 

第3部はいよいよ楽々ERDレッスン。ここでは、実際の例を元にERDを作成する過程を説明しています。ファストフード店でのレシートや病院の明細書兼領収書等をもとに説明されています。具体例があるので、まずは自分でERDをつくってみて、本書と照らし合わせてみるといった実践的な使い方もできます。

 

この本を読んでよかったことは、Railsが強制する各データにidを付与するということの重要性です。Railsを1年やってみて、その有用性を感じながらも経験者に裏付けされた理由は非常に納得感のあるものです。

また、単に教科書的なリレーショナルデータベースの知識ではなく、経営とも密接に結びついたデータベースを、ともするときれいな実装や理想ばかりにとらわれがちなエンジニアに、それをやる本来の目的を思い出してくれてくれます。

また、この本は初版が2006年ですが、10年をたった今でも色褪せないリレーショナルデータベースの技術であることを確信させてくれます。また、データベースの正規化をすると性能が悪くなるからあえて正規化しないという意見を持つ人もいますが、こういう立場に対して、

今のCPUのスペックは一体どれほどのものかということです。あるいはメモリです。RAMの容量がもう平気で1Gを超えられるようになっている状態で、ましてOSが64ビットに突入している状況において、20年ほど前のハードスペックを前提にした方法論をいまだに引きずっているというのは、ナンセンスではないのかということです。

(羽生章洋「楽々ERDレッスン」p 99)

 

と丁寧な口調でこき下ろしています。

10年前の書籍においてもこのように言われているのですから、まして2016年の今となっては、ということです。

 

最後に、前々回の記事でも書きましたが、@t_wadaさんに勧められたデータベース関連の書籍を改めて載せてこの記事を締めくくりたいと思います。

Rails 5でdraperを使う(2016年8月27日現在)

Rail 5でdraper(ActiveRecordをdecorateしてくれるgem)を使おうと思ったら、少しつっかかったのでメモ。
普通に、draperと書くと、

  gem 'draper'

2.1.0がinstallされてしまう。
そうすると、rails consoleとかしたときに、

/Users/user_name/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/draper-2.1.0/lib/draper.rb:5:in `require': cannot load such file -- active_model/serializers/xml (LoadError)

のエラーが出る。
これは、 active_model/serializers/xmlがgem化されて、デフォルトではインストールされなくなったからである。

参考:
Remove XML Serialization from core by zzak · Pull Request #21161 · rails/rails · GitHub
Rails5 に Draper 導入した際にエラーが出た - Yamakichi’s blog

それで、

  gem 'activemodel-serializers-xml', git: 'https://github.com/rails/activemodel-serializers-xml'

とすると、ここの部分は抜ける。
しかし、次にこのエラーが出る。

/Users/user_name/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/actionpack-5.0.0/lib/action_controller/test_case.rb:49:in `initialize': wrong number of arguments (0 for 2) (ArgumentError)

記事によっては、これは、rails5のブランチから持ってこれば良いという記事もあり、これでも動く。

gem 'draper', github: 'audionerd/draper', branch: 'rails5'

あるいは、3.0.0.pre1が公開されているので、それを使えば良いだろう。

gem 'draper', '>= 3.0.0.pre1'

これでbundle installをすると、

Installing activemodel-serializers-xml 1.0.1
Installing draper 3.0.0.pre1 (was 2.1.0)

と、activemodel-serializers-xmlも入れてくれる。もうすぐrails5対応のdraperが出るであろうからこの記事の賞味期限も短いだろうけれども。。

(2016/08/28 追記)
Rails 5でDraperを使ったら、ControllerからXxxxxDecoratorが呼べなかったorz...

Rubyのインスタンス変数の直接参照について

会社で議論になり、未だに私の中で決着がついていない問題を取り上げてみます。

Rubyインスタンス変数を直接参照することについてです。

例えば、コンストラクタでインスタンス変数を設定します。

そして、インスタンスメソッドでそのインスタンス変数を直接参照して処理をして値を返却するということをします。

良い例ではないかもしれませんが、こんな感じのイメージです。

class PriceCalculation
  def initialize(price, tax)
    @price = price
    @tax = tax
  end

  def tax_included_price
    @price * (1 + @tax)
  end
end

前提として、@priceや@taxはこのクラス内のみで使用され、外部からは参照されないものとします。

これに対し、同僚のドイツ人がインスタンス変数を直接参照するのは良くないから、attr_readerを使えと指摘してきました。 彼が言うにはこんな感じです。

class PriceCalculation
  attr_reader :price, :tax

  def initialize(price, tax)
    @price = price
    @tax = tax
  end

  def tax_included_price
    price * (1 + tax)
  end
end

しかし、私は、attr_readerを使ってしまうと、PriceCalculationの外部からpriceやtaxが参照される必要が無いにもかかわらず参照できてしまい、そちらのほうがよろしく無いのでと思いました。

インスタンス変数をそのまま参照する方法で、attr_readerを使わない場合は参照することはできません。

(もちろんrubyなので参照できないわけではないですが)

例1: この方法だと呼び出せない

[1] pry(main)> class PriceCalculation
[1] pry(main)*   def initialize(price, tax)
[1] pry(main)*     @price = price
[1] pry(main)*     @tax = tax
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   def tax_included_price
[1] pry(main)*     @price * (1 + @tax)
[1] pry(main)*   end
[1] pry(main)* end
=> :tax_included_price
[2] pry(main)> pc = PriceCalculation.new(200, 0.08)
=> #<PriceCalculation:0x007f9b41ae0ae0 @price=200, @tax=0.08>
[3] pry(main)> pc.price
NoMethodError: undefined method `price' for #<PriceCalculation:0x007f9b41ae0ae0 @price=200, @tax=0.08>
from (pry):12:in `__pry__'
[4] pry(main)> pc.tax
NoMethodError: undefined method `tax' for #<PriceCalculation:0x007f9b41ae0ae0 @price=200, @tax=0.08>
from (pry):13:in `__pry__'
[5] pry(main)> # rubyなので読み出す方法はもちろんあるが・・・。
[6] pry(main)> pc.instance_eval("@price")
=> 200

例1: この方法だと呼び出せる

[1] pry(main)> class PriceCalculation
[1] pry(main)*   attr_reader :price, :tax
[1] pry(main)*
[1] pry(main)*   def initialize(price, tax)
[1] pry(main)*     @price = price
[1] pry(main)*     @tax = tax
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   def tax_included_price
[1] pry(main)*     price * (1 + tax)
[1] pry(main)*   end
[1] pry(main)* end
=> :tax_included_price
[2] pry(main)> pc = PriceCalculation.new(200, 0.08)
=> #<PriceCalculation:0x007fa20222e830 @price=200, @tax=0.08>
[3] pry(main)> pc.price
=> 200
[4] pry(main)> pc.tax
=> 0.08

彼の言い分としては、これはパターンなのだと。attr_readerを使ったほうが変化に耐えうるソースコードなのだと、そう言っていました。そして、Sandi Metzの本を参照してきました。 GitHubから引用させていただきます。 poodr/chapter_2.rb at master · skmetz/poodr · GitHub

############## Page 24 ##############
class Gear
  def initialize(chainring, cog)
    @chainring = chainring
    @cog       = cog
  end

  def ratio
    @chainring / @cog.to_f      # <-- road to ruin
  end
end

############## Page 25 ##############
class Gear
  attr_reader :chainring, :cog  # <-------
  def initialize(chainring, cog)
    @chainring = chainring
    @cog       = cog
  end

  def ratio
    chainring / cog.to_f        # <-------
  end
end

road to ruinとは一直線に破滅に向かうという意味です。

その理由としては、インスタンス変数を編集してクラスで共通に使いたくなるときにメソッドだけ修正すればいいから変化に耐えうるということが挙げられていました。

############## Page 25 ##############
  # a simple reimplementation of cog
  def cog
    @cog * unanticipated_adjustment_factor
  end

############## Page 25 ##############
  # a more complex one
  def cog
    @cog * (foo? ? bar_adjustment : baz_adjustment)
  end

こんな風に@cogをそのまま使うのではなく、編集して使いたい時に、attr_readerを使ったほうが良いということでした。

ただ、私個人の意見としては、attr_readerで外から見えるようにすると、どこで使われているかすぐにはわからなくなり、保守性が下がってしまうこと、また、この例で言う@cogをPage 25の例のように何か処理をして使いたくなる場合は、ほとんどないと思うこと、あったとしてもすべてのcogを編集したcogで使いたいと思うケースはないと思うので、この利点についてはまったく利点と感じませんでした。むしろ、なにか処理をしたい場合はその処理を意味する名前を冠したメソッド名をつけておかないと何を意味するのかが皆目分からなくなってしまいます。

  def some_decorated_cog
    @cog * (foo? ? bar_adjustment : baz_adjustment)
  end

のように処理の意味を付け加えておかないと可読性が落ちます。

※コンストラクタで設定するということもできますが、これは良いのでしょうか、悪いのでしょうか・・・。

  def initialize(chainring, cog)
    @chainring = chainring
    @cog = cog * (foo? ? bar_adjustment : baz_adjustment)
  end

このあたり、私はSandi Metzの主張するパターンには同意しかねます。Sandi Metzの本には、このパターンについて2つの問題点が挙げられていました。今手元に無いので詳細は覚えていないのですが、もしかしたら、そちらの問題点のほうが重要なのではないかとさえ思いました。

そこで、いくつか参考例を見てみようと思いDraperのコードをあたってみました。

draper/factory.rb at master · drapergem/draper · GitHub

すると、attr_readerをprivateにするというやり方をしていました。

確かにこれだと外部から参照されないことが約束されるので、保守性が高まります。(黒魔術が使われない前提。)

[1] pry(main)> class PriceCalculation
[1] pry(main)*   def initialize(price, tax)
[1] pry(main)*     @price = price
[1] pry(main)*     @tax = tax
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   def tax_included_price
[1] pry(main)*     price * (1 + tax)
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   private
[1] pry(main)*
[1] pry(main)*   attr_reader :price, :tax
[1] pry(main)* end
=> nil
[2] pry(main)> pc = PriceCalculation.new(200, 0.08)
=> #<PriceCalculation:0x007fb6739cc3c8 @price=200, @tax=0.08>
[3] pry(main)> pc.price
NoMethodError: private method `price' called for #<PriceCalculation:0x007fb6739cc3c8 @price=200, @tax=0.08>
from (pry):16:in `__pry__'
[4] pry(main)> pc.tax
NoMethodError: private method `tax' called for #<PriceCalculation:0x007fb6739cc3c8 @price=200, @tax=0.08>
from (pry):17:in `__pry__'
[5] pry(main)> pc.tax_included_price
=> 216.0

正直、私はインスタンス変数になにかの処理をして同名のゲッターメソッドでそれを呼び出すことに大きな抵抗を感じるので、インスタンス変数を直接参照することにくらべてどのくらいの利点があるのだろうと思いました。 ゲッターメソッド経由で参照されるので処理も少し遅くなります。

このあたりの議論をrubyではないですが、C++での議論がありました。コメントでの議論も含めて考えさせられましたが、私はC++の素養がゼロなのであまり理解できておりません・・・。

メンバ変数を隠蔽する理由 - Qiita

この辺りも参考になりそうです。 http://qiita.com/Yahagi_pg/items/1bf59fc75d7f17c3b731

正直rubyでどう書くのがベストなのか、結論づけられていません・・・。

なお、車内で日本人エンジニア4人ほどと議論したところ、皆インスタンス変数直接参照すれば良いという意見でした。

※念のため繰り返しますが、インスタンス変数が外部から参照される必要が無い場合です。外部から参照される場合は、素直にattr_readerを使えば良いです。

達人に学ぶDB設計

久しく更新が止まっていましたが、久しぶりに更新です。

今日はデータベース関連の書籍を読み、とてつもない良書だったので紹介です。

その本がこちら。

@t_wadaさんに勧められて土日で一気に読破しました。

サブタイトルに「初級者で終わりたくないあなたへ」とありますが、データベースを軽く知っている程度(基本情報技術者試験で勉強した程度)でも、十分についていける内容です。

データベースを日頃から生業としている人にとっては、前半の正規化の説明などは釈迦に説法かと思いますが、改めて読むと復習も兼ねて得られるものも多いかと思います。

この正規化の話も含め前半はデータベースの基本的な話で、後半からがこの本のクライマックスになります。

「指南書」と銘打っておきながら、いきなりバッドノウハウが紹介されます。さらに、バッドノウハウが終わってもまだグレーノウハウが紹介されます。それだけデータベース設計の中にはバッドノウハウやグレーノウハウが満載ということなのでしょう。

きちんとデータベースの正規化をして、バッドノウハウやグレーノウハウにもなっていなければ、それなりに良いデータベース設計ということになるのではないでしょうか。

 

最後の方には、リレーショナルデータベースが不得意とするツリー構造の実装方法についても載っています。入れ子区間モデルの話は恥ずかしながら初めて出会ったので、ツリー構造を実装しなければいけない時が来たら、採用を検討してみたいと思います。

 

なお、最後の参考文献案内で

Bill Karwin 『SQL Antipatterns』

が紹介されており、「惜しむらくは、この本も邦訳がありません。」とありますが、@t_wadaさんががっつり訳してくれています。

ちなみに、他に@t_wadaさんがオススメしていたのは、

でした。これらの本も読み終わり次第、紹介していきたいと思います。