コンマで区切られた複数の外部キーを使用しているのは間違っていますか?


31

2つのテーブルがあります:DealDealCategories。1つの取引に多くの取引カテゴリを含めることができます。

したがって、適切な方法はDealCategories、次の構造で呼び出されるテーブルを作成することです。

DealCategoryId (PK)
DealId (FK)
DealCategoryId (FK)

ただし、アウトソースチームはDeal次の方法でテーブルに複数のカテゴリを保存しました。

DealId (PK)
DealCategory -- In here they store multiple deal ids separated by commas like this: 18,25,32.

彼らがしたことは間違っているように感じますが、なぜこれが正しくないのかを明確に説明する方法がわかりません。

これが間違っていることをどのように説明すればよいですか?それとも私が間違っているのかもしれませんが、これは受け入れられますか?


20
あなたが正しいです。データベース列にコンマ区切りリストを保存するのは本当に悪いですか?。簡単な答え:はい、それは悪いことです。
ypercubeᵀᴹ

7
アウトソーシングされたチームは、それ以上の損害を与える前にすぐに解雇します...(-_-)
ラファ

回答:


49

はい、それはひどい考えです。

行く代わりに:

SELECT Deal.Name, DealCategory.Name
FROM Deal
  INNER JOIN
     DealCategories ON Deal.DealID = DealCategories.DealID
  INNER JOIN
     DealCategory ON DealCategories.DealCategoryID = DealCategory.DealCategoryID
WHERE Deal.DealID = 1234

あなたは今行く必要があります:

SELECT Deal.ID, Deal.Name, DealCategories
FROM Deal
WHERE Deal.DealID = 1234

次に、アプリケーションコードで何かを行って、そのコンマリストを個々の番号に分割し、データベースを個別にクエリする必要があります。

SELECT DealCategory.Name
FROM DealCategory
WHERE DealCategory.DealCategoryID IN (<<that list from before>>)

このデザインのアンチパターンは、リレーショナルモデリングの完全な誤解(テーブルを怖がる必要はありません。テーブルは友達です。それらを使用してください)、またはカンマ区切りのリストを取得して分割する方が速いという奇妙に誤った信念に由来しますアプリケーションコードでは、リンクテーブルを追加することはありません(追加することはありません)。3番目のオプションは、外部キーをセットアップできるほどSQLに自信/能力がないことですが、その場合は、リレーショナルモデルの設計とは何の関係もありません。

SQL Antipatterns(Karwin、2010)は、このアンチパターン(彼は「Jaywalking」と呼んでいます)、15〜23ページに章全体を当てています。また、著者はSOで同様の質問を投稿してます。彼が(この例に適用されるように)指摘するキーポイントは次のとおりです。

  • 特定のカテゴリのすべての取引のクエリはかなり複雑です(その問題を解決する最も簡単な方法は正規表現ですが、正規表現はそれ自体が問題です)。
  • 外部キーの関係なしに参照整合性を強制することはできません。DealCategory nrを削除した場合。#26、アプリケーションコードで、カテゴリ#26への参照を探して各取引を調べ、それらを削除する必要があります。これはデータ層で処理されるべきものであり、アプリケーションで処理する必要があることは非常に悪いことです。
  • 集計クエリ(COUNTSUMなど)も、「複雑」から「ほぼ不可能」までさまざまです。すべてのカテゴリのリストとそのカテゴリの取引数のカウントを取得する方法を開発者に確認してください。適切に設計されていれば、それは4行のSQLです。
  • 更新はさらに難しくなります(つまり、5つのカテゴリに属する​​取引がありますが、2つを削除して他の3つを追加したい場合)。これは、適切な設計の3行のSQLです。
  • 最終的には、VARCHARリストの長さの制限に直面します。4000文字を超えるコンマ区切りのリストがある場合でも、とにかくモンスターが遅くなると解析する可能性があります。
  • データベースからリストを引き出し、それを分割し、別のクエリのためにデータベースに戻ることは、本質的に1つのクエリよりも遅くなります。

TLDR:それは根本的に欠陥のある設計であり、うまく拡張できず、最も単純なクエリでさえ複雑さを増し、すぐにアプリケーションを遅くします。


1
サイモン、誰かが同じ質問(dba.stackexchange.com/questions/17824/…)をしましたが、同じFKとPKが同じテーブルにある理由がわかりません。
jcho360

2
彼らがディールとカテゴリーの間に多対多の関係を持ちたいのか、それともカテゴリーのある種の階層を持ちたいのか、私には完全には分かりませんでした。いずれにせよ、それは主要なポイントの副次的なものでした。リンクテーブルの代わりにコンマ区切りのフィールドであることは悪い考えです。
サイモン・リガーツ

4

ただし、アウトソースチームは、次の方法でDealテーブルに複数のカテゴリを保存しました。

DealId(PK)DealCategory-ここには、18,25,32のようなコンマで区切られた複数の取引IDが格納されます。

特定の取引のカテゴリのみを照会する必要がある場合、これは実際には良い設計です。

しかし、特定のカテゴリのすべての取引を知りたい場合はひどいです。

また、更新、カウント、結合など、他のことを行うことが非常に難しく、エラーが発生しやすくなります。

非正規化には場所がありますが、同じデータに対して他のすべてのクエリを犠牲にして、1種類のクエリに対して最適化することに留意する必要があります。常に1つのパターンでクエリを実行することがわかっている場合は、非正規化されたデザインを使用すると利点が得られる場合があります。ただし、クエリの種類に柔軟性を必要とする可能性がある場合は、正規化されたデザインを使用してください。

他の形式の最適化と同様に、非正規化を正当化するかどうかを決定する前に、実行するクエリを知る必要があります。


1
コンマで区切られた子IDを持つ文字列が本当に役立つと思いますか?つまり、アプリケーションは最初に読み取り、次にIDを解析し、などのすべての子を照会する必要がありましたselect * from DealCategories where DealId in (1,2,3,4,...)。データベース設計に関して、私よりも多くの経験があります。そのため、非常に特殊なケースでそのような「極端なチューニング」を行う理由があるのか​​もしれません。これを正当化する唯一のアイデアは、selectDeal / DealCategoryの負荷が非常に高いことです。これは、テーブルの作成や作成を超えて、DB設計の知識がない外部委託チームのように見えます。
エリックハート

1
@ErikHartが、これは非正規化され、そしてそれができ有用であるが、私のポイントは、それはあなたが実行する必要があるクエリに完全に依存することです。正規化を解除すると、最適化される1つのクエリを除き、すべてのクエリのパフォーマンスが低下するのは当然です。その1つのクエリのみを実行する必要があり、他のクエリを気にしない場合、それは勝利です。しかし、これらはまれなケースです。通常、さまざまな方法でデータをクエリできる柔軟性が必要だからです。
ビルカーウィン14年

1
@ErikHartの場合、このアウトソースチームに、このデータに対するクエリを1つだけ含むプロジェクト仕様が与えられていれば、その特定のクエリに対してのみ最適化を設計できたはずです。言い換えれば、「あなたはそれを求めた、あなたはそれを得た」。しかし、アウトソーシングプロバイダーには、データの将来の使用を計画する理由はありません。仕様に書かれている内容の文字に合わせてアプリケーションを実装します。
ビルカーウィン14年

1

列の複数の値は、第1正規形に反しています。

また、テーブルはデータベースにリンクされるため、速度の向上はまったくありません。最初に文字列を読み取って解析し、次に「取引」のすべてのカテゴリを選択する必要があります。

正しい実装は、DealIdとDealCategoryIdを持つ「DealDealCategories」のようなジャンクションテーブルです。

不正な階層の実装?

また、別のDealCategoryに対するDealCategoriesのFKは、DealCategoriesの階層/ツリーの不適切な実装のように見えます。親ID(いわゆる隣接リスト)関係を介してツリーを操作するのは大変です!

階層を実装するときは、ネストされたセット(読みやすくはあるが修正が難しい)とクロージャーテーブル(全体的なパフォーマンスは最高だが、メモリ使用量が多い-おそらくDealCategoriesには多すぎない)をチェックしてください!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.