- PR -

1対1関連のテーブル設計について

投稿者投稿内容
だっちょ
大ベテラン
会議室デビュー日: 2006/12/05
投稿数: 115
投稿日時: 2008-03-24 18:04
案2で、bigintの代わりにList<Long>をバイナリにしてbyteaで格納しているだけです。
EntityのメンバとしてはList<Long>で格納してます。
jama
常連さん
会議室デビュー日: 2006/09/12
投稿数: 45
投稿日時: 2008-03-24 18:20
引用:

【案1】
ITEMテーブルにその商品が注目商品かどうかを示すカラム(boolean型)を設ける。
ただ、この方法だと「1つのカテゴリに最大1つの注目商品」という要件を
DBの制約で保証できない。
【案2】
CATEGORYテーブルにITEMテーブルへの外部キーであり、
nullableなカラム(NOTICE_ITEM_ID)を設ける。
ただこの場合、双方のテーブルが互いに外部参照することになる。
このような構造はおかしいのか?


【案1】はDBの制約で保証できない。
【案2】はDBの制約で保障できる。
様に受け取れますが、【案2】も保障できないのでは?
異なるカテゴリのアイテムを注目商品とする場合も考えられます。

案1,案2共に保障できないのであれば、案1がいいと思います。
べる
ぬし
会議室デビュー日: 2003/09/20
投稿数: 1093
投稿日時: 2008-03-24 22:28
案3、ただし複合キーにしてCATEGORY_IDに一意制約。
注目商品が複数登録される仕様に変わったら一意制約をとればいい。

でも、異なるカテゴリのアイテムを注目商品にできないという要件を
DBで保証したいなら案1がいいでしょうかね。
nikori
会議室デビュー日: 2008/03/13
投稿数: 11
投稿日時: 2008-03-25 03:39
たくさんのご意見ありがとうございます。

実は1対1関連に対する案2と3の使い分けが大きな疑問の1つでして、
案1はほとんど検討対象から外していたので、
案1を支持する方が多かったのは少し意外でした。
indigo-xさんのご指摘通り「ゆるい」仕様しか提示できなかったことによる
影響もあったのかもしれませんが ^^;
でも、そのおかげで案1も含めて皆さんの設計ポイントを
色々お聞きすることができて、とても参考になりました。

■jamaさん
ご指摘の通り、案1〜3のいずれのテーブルでも
データベースの制約だけで要件を完全に満たすことはできないですね。
見落としてました ^^;

■Anthyhimeさん、unibonさん
テーブルが相互に外部参照するというのはおかしなことでも、
アンチパターンでもないのですね。
レコード削除時に参照整合性違反でややことしいことになるのでは?
と思ったのですが、問題ないですね。


■よっしーさん
引用:

「最大1つ」という要件が今後も守られるなら案2、増えそうなら案1。
注目商品にその他の情報(期間とか)が必要なら案3が良いと思います。


とてもわかりやすいです

■indigo-xさん
案1に気持ちが傾いてきました

■忠犬さん
引用:

重複禁止の複合インデクス(マルチカラムインデクス)を定義すればいいのでは?


注目商品でない商品を複数登録できないように思ったのですが、どうなのでしょうか?

■だっちょさん
引用:

案2で、bigintの代わりにList<Long>をバイナリにしてbyteaで格納しているだけです。
EntityのメンバとしてはList<Long>で格納してます。


正直びっくりしました。
List<Long>からバイナリの変換はどのように行っているのでしょうか?
このようなレスポンス改善方法って結構採用されているものなのか、
自分も興味を持ちました。

■べるさん
引用:

案3、ただし複合キーにしてCATEGORY_IDに一意制約。
注目商品が複数登録される仕様に変わったら一意制約をとればいい。


なるほど、こういう方法もありますね。
この方法の利点は、仕様変更時のDBへの影響の少なさということでしょうか?

■まとめ
(1)今回の仕様では今後、注目商品が複数になることを見越して、
案1を採用しようと思います。
(2)「データベースの機能を使って制約すべき」という考えが強かったのですが、
皆さんのご意見をお聞きしてその考えがあまりにも強すぎていたように感じました
(暗黙的な制約は悪ではない)。
(3)よっしーさんにコメント頂いた指針をベースにみなさんのご意見を参考にしつつ、
自分の設計指針を構築していけたらと思いました。

みなさん、ご意見どうもありがとうございました。

[ メッセージ編集済み 編集者: nikori 編集日時 2008-03-25 03:42 ]
忠犬
大ベテラン
会議室デビュー日: 2006/05/01
投稿数: 109
投稿日時: 2008-03-26 18:37
引用:

注目商品でない商品を複数登録できないように思ったのですが、どうなのでしょうか?



例えば、同じカテゴリidに対して、trueとfalseは各々1件しか格納できません。
しかし、nullで格納すれば、重複エラーにはなりません。

つまり、「注目」の場合はtrue、「注目以外」はnullで格納すれば、DBMS側で制約を
設けられます。
カーニー
ぬし
会議室デビュー日: 2003/09/04
投稿数: 358
お住まい・勤務地: 東京
投稿日時: 2008-03-27 12:15
引用:

忠犬さんの書き込み (2008-03-26 18:37) より:
例えば、同じカテゴリidに対して、trueとfalseは各々1件しか格納できません。
しかし、nullで格納すれば、重複エラーにはなりません。

つまり、「注目」の場合はtrue、「注目以外」はnullで格納すれば、DBMS側で制約を
設けられます。


ここら辺、実はRDBMSによってだいぶ実装が異なります。
通常は「NULLとNULLは等しくない」ので、複数レコードがユニークキー列にNULL値を持てるはずなのですが、例えばMS SQL ServerやDB2の一意制約の実装では「NULLとNULLは等しい」ので、最大1レコードしかNULL値を持てません。

Oracleは基本的に「NULLとNULLは等しくない」のですが、マルチカラムユニークキーの一部の列がNULLの場合に限り、「NULLとNULLは等しい」と扱われる場合があります。つまり、
(NULL) != (NULL)
('A', 'B', NULL) = ('A', 'B', NULL)
(NULL, NULL, NULL) != (NULL, NULL, NULL)
です。

知っている限りでは、MySQLとPostgreSQLはNULLに忠実です。
('A', 'B', NULL) != ('A', 'B', NULL)

元質問の対象であるHSQLDBについては、残念ながら知りませんが。

[ メッセージ編集済み 編集者: カーニー 編集日時 2008-03-27 12:20 ]
よっしー
大ベテラン
会議室デビュー日: 2007/05/17
投稿数: 143
投稿日時: 2008-03-27 13:32
引用:

カーニーさんの書き込み (2008-03-27 12:15) より:
(NULL) != (NULL)
('A', 'B', NULL) = ('A', 'B', NULL)
(NULL, NULL, NULL) != (NULL, NULL, NULL)


Oracleは一意制約の実装にユニークインデックスを使ってるからですね。キーがNullだけのインデックスは作らないので。
SQLServerは「Nullが重複したらだめ」ですが、Nullは重複してもOKの方が使い勝手が良いと思います。「1レコードだけNullを許す」なんて要件もあまりないでしょうし。
カーニー
ぬし
会議室デビュー日: 2003/09/04
投稿数: 358
お住まい・勤務地: 東京
投稿日時: 2008-03-27 14:38
引用:

よっしーさんの書き込み (2008-03-27 13:32) より:
Oracleは一意制約の実装にユニークインデックスを使ってるからですね。キーがNullだけのインデックスは作らないので。


その意味では、僕が知っている限りにおいて、どのデータベースも一意制約を作成すると、自動的にユニークインデックスが作成されます。
なので、データベースによってユニークインデックスにおけるNULLの扱い方が異なる、と言えます。

引用:

SQLServerは「Nullが重複したらだめ」ですが、Nullは重複してもOKの方が使い勝手が良いと思います。「1レコードだけNullを許す」なんて要件もあまりないでしょうし。


僕もRDBMSはOracleから入ったので、そう思うんですがね。
実際のところSQL2003において「必須」とされているのは、「NULL値を許容しない一意制約」だけで、「NULL値を許容する一意制約」は「オプショナル」です。実装する場合のNULL取り扱い方法が定義されているのかどうかは、残念ながら知りません。

またSQL標準においては、一意インデックスはおろか、インデックスそのものについてすら言及されていません。

これはなかなかにややこしい問題なのです。

一番混乱がないのは、NOT NULL列にしか一意制約を定義しない、ということになるのだと思います。

スキルアップ/キャリアアップ(JOB@IT)