<?xml version="1.0" encoding="UTF-8" ?>
<entry
	xmlns="http://www.w3.org/2005/Atom"
	xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
	xml:lang="ja-JP"
>
	<title>1:nなデータを検索しやすくDBに入れたい</title>
	<id>tag:txqz.net,2008-03-20:blog/2008/03/20/1834</id>
	<link rel="self" href="http://txqz.net/blog/2008/03/20/1834.atom"/>
	<link rel="alternate" type="application/rss+xml" href="http://txqz.net/blog/2008/03/20/1834.rdf"/>
	<link rel="alternate" type="application/xhtml+xml" href="http://txqz.net/blog/2008/03/20/1834.xhtml"/>
	<link rel="alternate" type="text/html" href="http://txqz.net/blog/2008/03/20/1834.html"/>
	<link rel="contents" href="http://txqz.net/blog/2008/03/20/.atom" title="2008年3月20日"/>
	<link rel="first" href="http://txqz.net/blog/2001/08/04/0001.atom" title="地球空冷化"/>
	<link rel="prev" href="http://txqz.net/blog/2008/03/14/2312.atom" title="第5回Googleデベロッパー交流会に参加した"/>
	<link rel="next" href="http://txqz.net/blog/2008/04/06/1139.atom" title="エクスプレス予約がリニューアルごとに使いにくくなる件"/>
	<link rel="last" href="http://txqz.net/blog/2008/12/19/2152.atom" title="浜松市街地を通り抜けて、ムーンライトながら～の思い出"/>
	<author>
		<name>陽坂智佐</name>
		<email>spambasket@txqz.net</email>
	</author>
	<content type="xhtml">
		<div xmlns="http://www.w3.org/1999/xhtml">
<p>いま後輩が悩んでいるネタ。たとえば、以下のようなデータをデータベースにどう格納するかを考える。</p>
<table>
<caption>夜行バスと割引</caption>
<thead>
<tr><th>名前</th><th>行き先</th><th>片道運賃</th><th>割引</th></tr>
</thead>
<tbody>
<tr><th>セレナーデ号</th><td>広島駅</td><td>8400</td><td>早売14、ネット割2%、往復割引</td></tr>
<tr><th>出雲・松江ドリーム名古屋号</th><td>出雲市駅</td><td>9000</td><td>早売21、ネット割2%、往復割引</td></tr>
<tr><th>オリーブ松山号</th><td>JR松山支店</td><td>10000</td><td>往復割引</td></tr>
</tbody>
</table>
<p>手元に資料があるもので適当に問題を置き換えたので例が悪いが、とにかくこういうデータをどういうスキーマで格納するかを考える。教科書どおりに沿って考えると、このデータは正規化されていないので、正規形に変える必要があるだろう。</p>
<table>
<caption>夜行バス</caption>
<thead>
<tr><th>ID</th><th>名前</th><th>行き先</th><th>片道運賃</th></tr>
</thead>
<tbody>
<tr><th>1</th><td>セレナーデ号</td><td>広島駅</td><td>8400</td></tr>
<tr><th>2</th><td>出雲・松江ドリーム名古屋号</td><td>出雲市駅</td><td>9000</td></tr>
<tr><th>3</th><td>オリーブ松山号</td><td>JR松山支店</td><td>10000</td></tr>
</tbody>
</table>
<table>
<caption>割引</caption>
<thead>
<tr><th>ID</th><th>割引</th></tr>
</thead>
<tbody>
<tr><th>1</th><td>早売14</td></tr>
<tr><th>1</th><td>ネット割2%</td></tr>
<tr><th>1</th><td>往復割引</td></tr>
<tr><th>2</th><td>早売21</td></tr>
<tr><th>2</th><td>ネット割2%</td></tr>
<tr><th>2</th><td>往復割引</td></tr>
<tr><th>3</th><td>往復割引</td></tr>
</tbody>
</table>
<p>このままデータベースに突っ込めば、「ネット割2%」が適用されるバスの情報が欲しければ以下のSQL文で参照できるはず:</p>
<pre><code class="sql">SELECT 名前 FROM 夜行バス LEFT JOIN 割引 USING(id) WHERE 割引='ネット割2%';</code></pre>
<p>問題はここからで、このスキームに従うと、「早売14とネット割2%の両方が指定されている夜行バス」の一覧が欲しいときに割引テーブルを自己結合するか、早売14が指定されているバスの集合とネット割2%が指定されているバスの集合を求めて積集合を取るかしないといけなくなる。指定が3つ4つと増えていくと計算量がみるみる増えていくので、あまり現実的な話ではない。</p>
<p>MySQLには<a href="http://dev.mysql.com/doc/refman/4.1/ja/set.html" title="MySQL :: MySQL 4.1 リファレンスマニュアル :: 6.2.3.4 SET 型">SET型</a>というものがあるので、これからもずっとMySQLで運用する気なら、割引カラムをSET型で宣言すればよい。</p>
<pre><code class="sql">CREATE TABLE 夜行バス (
  id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  名前 VARCHAR(20) NOT NULL,
  行き先 VARCHAR(15) NOT NULL,
  片道運賃 SMALL INT UNSIGNED NOT NULL
  割引 SET('早売14','早売21','ネット割2%','往復割引')
);</code></pre>
<p>「早売14とネット割2%の両方が指定されている夜行バス」は以下のSQL文で求められるはず:</p>
<pre><code class="sql">SELECT 名前 FROM 夜行バス WHERE FIND_IN_SET('早売14',割引) AND FIND_IN_SET('ネット割2%',割引);</code></pre>
<p>ただ、どうせMySQLでいくなら、割引カラムにFulltextインデックスをつけてmatch() against() したほうが楽だったりして。</p>
<p>問題はほかにもあって、なんか元データはエクセルに以下のような感じで収まっているのだという:</p>
<table>
<thead>
<tr><th>名前</th><th>行き先</th><th>片道運賃</th><th>早売14</th><th>早売21</th><th>ネット割2%</th><th>往復割引</th></tr>
</thead>
<tbody>
<tr><td>セレナーデ号</td><td>広島駅</td><td>8400</td><td>○</td><td></td><td>○</td><td>○</td></tr>
<tr><td>出雲・松江ドリーム名古屋号</td><td>出雲市駅</td><td>9000</td><td></td><td>○</td><td>○</td><td>○</td></tr>
<tr><td>オリーブ松山号</td><td>JR松山支店</td><td>10000</td><td></td><td></td><td></td><td>○</td></tr>
</tbody>
</table>
<p>確かにそれは面倒だ。強まった人なら1発でいい感じに整形できるのだろうけど、私だったら正規表現の使えるエディタにコピペして</p>
<pre>s/○$/往復割引/g
s/○(\t[^\t]*)$/ネット割2%\1/g
s/○(\t[^\t]*)(\t[^\t]*)$/早売21\1\2/g
s/○(\t[^\t]*)(\t[^\t]*)(\t[^\t]*)$/早売14\1\2\3/g</pre>
<p>と置換するかなあ。置換すべき項目がもっと多いならもう少しエレガントな方法を考えるけど、10個くらいだったら上のようなやり方でやっちゃったほうが早そう。</p>
<ins class="ps" datetime="2008-03-20T23:30:40+09:00" id="PS20080320233040">
<p></p>
<blockquote title="はてなブックマーク - hijiのブックマーク / 2008年03月20日" cite="http://b.hatena.ne.jp/hiji/20080320#bookmark-7947357">
<p>set型か。覚えておこう。あとどうでもいいが、早「売」14。→<a href="http://www.kousokubus.net/PC/BPGD/BPGD221_01.htm">http://www.kousokubus.net/PC/BPGD/BPGD221_01.htm</a> というか、実在のデータじゃないのね。</p>
</blockquote>
<p>これはやってしまいましたね。修正しました。ご指摘ありがとうございました。あとデータはJR東海バスの『JRハイウェイバス時刻表2008.3.1→6.30』の26ページから引用しました。</p>
</ins>
		</div>
	</content>
	<category term="MySQL"/>
	<category term="SQL"/>
	<category term="データベース"/>
	<category term="正規表現"/>
	<trackback:ping>http://txqz.net/blog/2008/03/20/1834/tb</trackback:ping>
	<published>2008-03-20T18:34:29+09:00</published>
	<updated>2008-03-20T23:30:40+09:00</updated>
	<rights>Attribution-Noncommercial-Share Alike 3.0 Unported</rights>
</entry>