web-dev-qa-db-ja.com

テーブルの親子関係をより適切に定義する

私たちのチームは新しいシステムを開発しており、顧客には1次、2次、3次、およびN次の倉庫が存在する可能性があるため、エントリ間に階層関係を持つ必要があるWarehouseテーブルがあります。

this が最良の方法だと最初に思いました。テーブルにParent列とChild列を作成し、Parentを(同じテーブル上で)Child to FKにすることで、友人DBAは別の方法があると私たちに言った。

たとえば、次のようなテーブルを作成することを考えていました。

CREATE TABLE Warehouse(
filterId INT PRIMARY KEY IDENTITY,
parentId INT NOT NULL,
active BOOL DEFAULT 1,
warehouseName VARCHAR(30) NOT NULL,
FOREIGN KEY parentId REFERENCES Warehouse(filterId)

| FILTERID   | PARENTID | ACTIVE | WAREHOUSENAME |
--------------------------------------------------
|          1 |        0 |      1 | N WAREHOUSE 1 |
|          2 |        1 |      1 | ROOM 1        |
|          3 |        1 |      1 | ROOM 2        |
|          4 |        1 |      1 | ROOM 3        |
|          5 |        1 |      1 | ROOM 4        |
|          6 |        2 |      1 | SHELF 1       |
|          7 |        2 |      1 | SHELF 2       |
|          8 |        2 |      1 | SHELF 3       |
|          9 |        2 |      1 | SHELF 4       |
|         10 |        2 |      1 | SHELF 5       |
|         11 |        2 |      1 | SHELF 6       |
|         12 |        2 |      0 | SHELF 7       |
|         13 |        3 |      1 | BOX 1         |
|         14 |        3 |      0 | BOX 2         |
|         15 |        3 |      1 | BOX 3         |
|         16 |        3 |      1 | BOX 4         |
--------------------------------------------------

しかし、このDBAは、次のような作業を行う必要があると述べています。

CREATE TABLE Warehouse(
filterId VARCHAR(20) PRIMARY KEY,
active BOOLEAN DEFAULT 1,
warehouseName VARCHAR(30) NOT NULL)
| FILTERID   | ACTIVE | WAREHOUSENAME |
---------------------------------------
| 1.00       |      1 | N WAREHOUSE 1 |
| 1.01       |      1 | ROOM 1        |
| 1.02       |      1 | ROOM 2        |
| 1.03       |      1 | ROOM 3        |
| 1.04       |      1 | ROOM 4        |
| 1.01.01    |      1 | SHELF 1       |
| 1.01.02    |      1 | SHELF 2       |
| 1.01.03    |      1 | SHELF 3       |
| 1.01.04    |      1 | SHELF 4       |
| 1.01.05    |      1 | SHELF 5       |
| 1.01.06    |      1 | SHELF 6       |
| 1.01.07    |      0 | SHELF 7       |
| 1.01.01.01 |      1 | BOX 1         |
| 1.01.01.02 |      0 | BOX 2         |
| 1.01.01.03 |      1 | BOX 3         |
| 1.01.01.04 |      1 | BOX 4         |
---------------------------------------

どちらの方向に進むべきですか?

一方では、parentIdfilterId;の間に関係があります。もう一方には関係はありませんが、エントリがどこに属しているかは簡単に理解できます。

すべての倉庫が同じレベルにあるわけではありません-それは私がある程度意味するところです。顧客Aの場合、1は1.1と1.2の親、2は2.1の親、3は3.1の親、4は4.1と4.2の親ですが、1、2、3、4は同じですレベル(第1レベル)、および1.1、1.2、2.1、3.1、4.1、4.2も同じレベル(第2レベル)ですが、同じ親ではありません。

2
Human_AfterAll

リレーショナルデータベースには、階層データのモデルがいくつかあります。これらには

  1. 隣接リスト。これは上記の提案です。
  2. パス列挙。これはDBAの提案です。
  3. ネストされたセット
  4. 閉鎖テーブル

それぞれに、異なる挿入、削除、隣接読み取り、および設定読み取りの特性があります。平均して、アプリケーションのパフォーマンスと開発のニーズに最適なものを決定する必要があります。たとえば、入れ子になったセットは、サブツリー全体を読み取るのに最適ですが、挿入にはコストがかかり、理解するのが難しい場合があります。隣接リストは理解しやすく、コーディングも簡単です。読み取り/書き込みは高速ですが、頻繁に行われる大規模な再帰クエリはコストがかかる可能性があります。

SQL Serverを使用しているので、 hierarchyid データ型に注意してください。

Joe Celkoの著書「SQL for Smartiesのツリーと階層」が最も読みやすく、参考になることがわかりました。

Stackoverflow に関する議論があり、これについて広く議論しています。

2
Michael Green

あなたの質問では「最高のパフォーマンス」について尋ねますが、どのようなクエリに関してですか?どちらが典型的なクエリなのか、典型的なクエリなのかは明確ではないので、私の答えは一般的なものになります。

この場合の最良のスキーマは次のとおりだと思います。

Relation Customer, with attributes:
  idCustomer, integer primary key,
  other attributes...;

Relation Warehouse, with attributes:
  idWarehouse, integer primary key,
  idCustomer, integer, foreign key for Customer,
  idParentWarehouse, integer, foreign key for Warehouse,
  warehouseLevel, varchar(10),
  warehouseName, varchar(100),
  other attributes...
  constraint (idCustomer, warehouseLevel) unique;

Warehouseのインデックスは、プライマリキーと外部キー、および(idCustomer、warehouseLevel)にあります。

ここで、warehouseLevelは1、1.2、2.1.3などの文字列にすることができます。

このようなスキームを使用すると、簡単に見つけることができます。

1)顧客のすべての倉庫、

2)特定の倉庫の親倉庫、

3)特定の倉庫のすべての子倉庫、

4)特定の倉庫のすべての子孫倉庫(再帰クエリを使用)。

さらに、顧客の特定の程度のすべての倉庫も検索する必要がある場合は、(idCustomer、degree)にインデックスを付けた属性の程度、整数をWarehouseに追加する必要があります。

2
Renzo