※ながめだよ
先日、PSStoreのセールにつられて「俺に働けって言われても乙 HD」を買った。 ほんのりプレイしていたところ、『このデータを表現するテーブルはどんな構造だ?』と気になったので考えた。 考えたので残すことにした。
経緯
ゲームシステムのひとつに「施設建設」というものがある。 武器屋や採掘場を建設し、新しい武器の開発や素材生産で冒険をサポートするのだ。
さらに、特定の施設を隣接させると「シナジー効果」が発生する。 武器屋の隣に鍛冶屋を建てれば「武器の性能が5%UP」する、といった具合だ。
このシナジー効果、建物によって発生したりしなかったりする上、UI上の問題でシナジー効果の確認が少々面倒くさい。 ので、スプレッドシートにメモを取ろうとしたところ、どういう構造なのか疑問を持った。
テーブル考察
RDBのテーブルとして定義した場合を考える。これは趣味だ。
(KVSとかドキュメントストアの考え方知りたい)
前提情報
状況
すでに武器屋が建設済みで、その隣に鍛冶屋を建設するという状況で進めていく。
マスタ
施設と効果について、以下のようにマスタが区切られている。 IDカラムは主キーである。
施設マスタ
ID | 施設名 |
---|---|
1 | 武器屋 |
2 | 防具屋 |
3 | 道具屋 |
4 | 鍛冶屋 |
… | … |
効果マスタ
ID | 効果内容 |
---|---|
1 | 武器の性能が5%UP |
2 | 施設維持費300J/日 |
… | … |
複合主キー?サロゲートキー?
前述のマスタに登場するID
カラムを除き、主キーを明言しない。
パターン1:とにかく最小で必要なレコードを追加したパターン
施設ID1 | 施設ID2 | 効果ID |
---|---|---|
1 | 4 | 1 |
1 | 4 | 2 |
… | … | … |
とりあえず作りましたパターン。 効果マスタのJOINはするかしないか流派があるので割愛するが、必要となるSQLは以下のような感じだろうか。
SELECT * FROM シナジー効果 WHERE 施設ID1=4 AND 施設ID2=1; -- 最小で必要なレコードしかないため、施設IDを入れ替えての確認もする SELECT * FROM シナジー効果 WHERE 施設ID1=1 AND 施設ID2=4;
所感
- アプリケーション側が2度SQLを発行するのが喧嘩の種になりそう
- 効果IDが同じで、施設IDが入れ替ったレコードがさらに追加される危険がある
- 効果IDをユニークにしてしまう対策
- 施設維持費加算の効果レコードが、対象の施設ごとに必要になる
- 施設IDが小さい方が常に施設ID1になるようデータ登録する運用対策
- コード側での比較処理が必要
- 負担は減る
- 間違って登録したときに、施設ID1に大きなものが入ってることを認識していないと、調査が困難
- 間違って登録してしまうのに、調査で間違いに気づけるかどうか……
- 「とりあえず正しいレコード足しとけ」で解決はできる
- 効果IDをユニークにしてしまう対策
パターン2:コードの簡素化を優先し、似たようなレコードを登録したパターン
施設ID | 隣接施設ID | 効果ID |
---|---|---|
1 | 4 | 1 |
1 | 4 | 2 |
4 | 1 | 1 |
4 | 1 | 2 |
… | … | … |
これから建設しようとしてる施設と、建設済みの施設が明確なことから、実装作業の簡素化を目的にテーブル側に苦労させるパターン。 SQLは一度だけ実行すれば良いのでシンプル。
SELECT * FROM シナジー効果 WHERE 施設ID=4 AND 隣接施設ID=1;
所感
- 登録作業がめんどくさそう
- 登録補助がないとやりたくない
- 管理画面なり表計算ソフトなりでのサポートさえできればよくある選択肢ではないだろうか
パターン3:効果IDを主体にするパターン
効果ID | 施設ID |
---|---|
1 | 1 |
1 | 4 |
2 | 1 |
2 | 4 |
… | … |
組み合わせに順番なんか必要ないという発想を体現したパターン。SQLはちょっと豪快。
SELECT DISTINCT シナジー効果.効果ID FROM シナジー効果 INNER JOIN シナジー効果 AS temporary ON シナジー効果.効果ID = temporary.効果ID AND temporary.施設ID = 1 --隣接施設ID WHERE シナジー効果.施設ID=4 ; -- あるいは SELECT 効果ID, COUNT(*) AS RecordCount FROM シナジー効果 WHERE 施設ID IN (1, 4) GROUP BY 効果ID HAVING RecordCount > 1 ;
所感
- 仕様に胡座をかいてる感がある
- 効果を使い回すことができなくなるので、同じ効果だが組み合わせが違うケース1件につきひとつ、効果レコードが必要になる
効果を使い回せない問題
例えば防具と鍛冶屋の組み合わせ。この場合、防具の効果5%UP
と施設維持費300J/日
が発生する。
この内、5%UPを効果ID=3
とし、維持費は効果ID=2
を使い回そうとして、以下の様なテーブルを作ったとする。
効果ID | 施設ID |
---|---|
1 | 1 |
1 | 4 |
2 | 1 |
2 | 4 |
2 | 2 |
3 | 2 |
3 | 4 |
… | … |
そして次のようなSQLをかけると
SELECT 効果ID, COUNT(*) AS RecordCount FROM シナジー効果 WHERE 施設ID IN (1, 2) GROUP BY 効果ID HAVING RecordCount > 1 ;
武器屋と防具屋の間にシナジー効果が発生し、ただ維持費をむしり取られる展開になってしまう。
パターン4:パターン3を発展、組み合わせという概念を定義するパターン
組み合わせマスタ
組み合わせID |
---|
1 |
… |
施設組み合わせ
組み合わせID | 施設ID |
---|---|
1 | 1 |
1 | 4 |
… | … |
組み合わせID | 効果ID |
---|---|
1 | 1 |
1 | 2 |
… | … |
シナジー効果は施設の組み合わせに紐づくため、新たに組み合わせマスタを定義したパターン。 SQLは以下の様になるだろう。
SELECT * FROM ( SELECT 組み合わせID, COUNT(*) AS RecordCount FROM 施設組み合わせ WHERE 施設ID IN (1, 2) GROUP BY 組み合わせID HAVING RecordCount > 1 ) AS 組み合わせ INNER JOIN シナジー効果 ON 組み合わせ.組み合わせID = シナジー効果.組み合わせID ;
所感
- SQLながい
- 施設の順番も、余分なレコードも討ち取った