通信会社に勤めていてルーターやスイッチに収容されているユーザー回線数を集計する簡単なWebアプリを作っています。最終的にこんな感じの表を出力させたいです。
rt_host | rt_port | sw1_host | sw1_port | sw2_host | sw2_port | users |
---|---|---|---|---|---|---|
RT01 | Gi0/0/0 | - | - | - | - | 10 |
RT01 | Gi0/1/0 | SW01 | Gi1/1 | - | - | 8 |
RT01 | Gi0/1/0 | SW01 | Gi1/2 | - | - | 14 |
RT01 | Gi0/1/1 | SW02 | Gi1/1 | - | - | 7 |
RT02 | Gi0/0/0 | SW03 | Gi1/1 | - | - | 53 |
RT02 | Gi0/0/0 | SW03 | Gi8/1 | SubSW01 | Gi0/1 | 11 |
-
になっているところがありますが、実は
- RT --- Aプランユーザー
- RT --- SW --- Bプランユーザー
- RT --- SW --- SubSW --- Cプランユーザー
といった具合に契約プラン等によってネットワークの階層数が異なっており、頭を抱えています。
上表をそのままDBにするとNULLだらけになってしまう上に主キーも張れず悪手に思えます。-
などのNULL代替ワードを使うことも検討しましたが、xxx_host カラムにホスト情報テーブルに対する外部キー制約をかけたいので避けたいところです。
このように深さが可変のツリー構造(複合主キーにするカラム数が可変)な場合どうテーブルを設計すべきでしょうか?具体的なスキーマではなく、考え方やデザインパターンの名称でも構いません。
条件
- ネットワーク機器の階層は2〜4(例が雑多になるので本質問では最大3で取り扱っています)
- 'SW03' の例のように、SW03 に直接ぶらさがっているユーザーが居るし、その下に 'SubSW01' があってそのさらに下にぶらさがっているユーザーも居る場合があります。と末端にだけデータノードが付いたシンプルなツリー構造ではありません
- アプリの目的: ネットワーク機器のつながり情報の閲覧と、「SW01は全部で22ユーザー」のように任意カラムでのユーザー数集計
- 環境: MySQL + Python (Flask + SQLalchemy)
以下は自分に考えてみた案です。長いので無視して構いません。
代案1:通信機器のつながり情報とユーザー収容情報を分け再帰検索
neighbors テーブル:
self_host(PK) | self_port(PK) | dest_host | dest_port |
---|---|---|---|
RT01 | Gi0/1/0 | SW01 | Gi1/1 |
RT01 | Gi0/1/1 | SW02 | Gi1/1 |
RT02 | Gi0/0/0 | SW03 | Gi8/1 |
SW03 | Gi8/1 | SubSW01 | Gi0/1 |
users テーブル(ユーザーに隣接するホストにユーザー数情報を紐付け)
last_host(PK) | last_port(PK) | users |
---|---|---|
RT01 | Gi0/0/0 | 10 |
SW01 | Gi1/1 | 8 |
SW01 | Gi1/2 | 14 |
SW02 | Gi1/1 | 7 |
SW03 | Gi1/1 | 11 |
SubSW01 | Gi0/1 | 23 |
たとえば "SW03" で検索した場合、
- users テーブルを
'SW03'
で検索:ヒット有り('SW03', 'Gi1/1', 11
) → 一部結果取得 - neighbors テーブルの self_host を
'SW03'
で検索:ヒット有り(SW03', 'Gi8/1', 'SubSW01, 'Gi0/1'
) - users テーブルを
'SubSW01', 'Gi0/1'
で検索:ヒット有り('SubSW01', 'Gi0/1', 23
) → 一部結果取得 - neighbors テーブルの self_host を
'SubSW01', 'Gi0/1'
で検索:ヒット無し → 下方向探索終了 - neighbors テーブルの dest_host を
'SW03'
で検索:ヒット有り('RT02', 'Gi0/0/0', 'SW03', 'Gi8/1'
) - neighbors テーブルの dest_host を
'RT02', 'Gi0/0/0'
で検索:ヒット無し → 上方向探索終了、情報確定 - 上記で取り出した情報からHTMLの表を作成し表示
と処理が非常に煩雑になってしまいます。問い合わせ回数が多く、アプリ側で親・子・孫のどのレイヤーの情報なのか分からない上に、集計処理(GROUP BY + SUM)を自前で行う必要があり、「リレーショナルデータベースを使う意味があるのか?? もうNULLだらけでもいいんじゃないか!?」と思い始めています。
代案2. 階層ごとにテーブルを分けて外部結合
最大階層数が3なのでつながり情報のテーブルを2つ用意し外部結合して検索
neighbors1 テーブル:
self_host(PK) | self_port(PK) | dest_host | dest_port |
---|---|---|---|
RT01 | Gi0/0/0 | NULL | NULL |
RT01 | Gi0/1/0 | SW01 | Gi1/1 |
RT01 | Gi0/1/1 | SW02 | Gi1/1 |
RT02 | Gi0/0/0 | SW03 | Gi8/1 |
neighbors2 テーブル:
self_host(PK) | self_port(PK) | dest_host | dest_port |
---|---|---|---|
SW01 | Gi1/1 | NULL | NULL |
SW01 | Gi1/2 | NULL | NULL |
SW02 | Gi1/1 | NULL | NULL |
SW03 | Gi1/1 | NULL | NULL |
SW03 | Gi8/1 | SubSW01 | Gi0/1 |
users テーブル
(略)
実装は案1よりシンプルになりそうですが、それでも neighbors1 の self、neighbors2 の self、neighbors2 の dest とそれぞれJOINした3つの users テーブルの内容をアプリ側で合算する必要があり、なんだかなーという感じです。。。
以上、雑文で申し訳ありませんがアドバイスよろしくお願いします。

回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/12/21 07:26