web-dev-qa-db-ja.com

データベース列に区切りリストを保存するのは本当に悪いですか?

一連のチェックボックスがあるWebフォームを想像してください(いずれかまたはすべてを選択できます)。データベーステーブルの1つの列に格納されている値のコンマ区切りリストに保存することを選択しました。

今、私は正しい解決策が2番目のテーブルを作成し、データベースを適切に正規化することであることを知っています。簡単なソリューションを実装する方が迅速であり、私はそのアプリケーションの概念実証を、あまりにも多くの時間を費やすことなく、迅速に実現したかったのです。

節約された時間とシンプルなコードは私の状況では価値があると思いましたが、これは防御可能な設計の選択肢ですか、それとも最初から標準化する必要がありましたか?

もう少しコンテキスト、これは共有フォルダーに保存されたExcelファイルを本質的に置き換える小さな内部アプリケーションです。私はまた、プログラムをクリーンアップし、より保守しやすくすることを考えているので、質問しています。そこには、私が完全に満足しているわけではないものがいくつかあります。そのうちの1つは、この質問のトピックです。

338
Mad Scientist

最初の正規形 に違反することに加えて、単一の列に格納された値の繰り返しグループのために、コンマ区切りリストには、より多くのより実用的な問題があります。

  • 各値が正しいデータ型であることを保証できません:1,2,3、banana、5を防ぐ方法はありません
  • 外部キー制約を使用して値をルックアップテーブルにリンクすることはできません。参照整合性を強制する方法はありません。
  • 一意性を強制できない:1,2,3,3,3,5を防ぐ方法はありません
  • リスト全体を取得せずにリストから値を削除することはできません。
  • 文字列の列に収まるリストより長くリストを保存することはできません。
  • リスト内の特定の値を持つすべてのエンティティを検索するのは困難です。非効率的なテーブルスキャンを使用する必要があります。 MySQLのように、正規表現に頼らなければならない場合があります。
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • リスト内の要素をカウントしたり、他の集計クエリを実行することは困難です。
  • 値が参照するルックアップテーブルに値を結合するのは困難です。
  • ソートされた順序でリストを取得するのは難しい。

これらの問題を解決するには、大量のアプリケーションコードを記述し、RDBMSが既により効率的に提供する機能を再発明する必要があります

コンマ区切りのリストは十分に間違っているため、これを私の本の最初の章にしています。 SQLアンチパターン:データベースプログラミングの落とし穴の回避

非正規化を使用する必要がある場合もありますが、 @ OMG Ponies言及 のように、これらは例外的なケースです。非リレーショナルの「最適化」は、データのその他の使用を犠牲にして1種類のクエリに利益をもたらすため、非正規化に値するほど特別に処理する必要があるクエリを知っていることを確認してください。


* MySQL 8.0は、このWord境界式構文をサポートしなくなりました。

529
Bill Karwin

SOの質問には多くの質問があります:

  • コンマ区切りリストから特定の値のカウントを取得する方法
  • そのコンマ区切りリストから同じ2/3/etc特定の値のみを持つレコードを取得する方法

コンマ区切りリストの別の問題は、値の一貫性を確保することです-テキストを保存すると、タイプミスの可能性があります...

これらはすべて非正規化データの症状であり、常に正規化データをモデル化する必要がある理由を強調しています。非正規化canはクエリの最適化、必要性が実際に現れるときに適用される

39
OMG Ponies

「1つの理由は怠でした」。

これにより、警告音が鳴ります。このようなことをする必要がある唯一の理由は、「正しい方法」でそれを行う方法を知っているが、そのようにしない具体的な理由があるという結論に達したからです。

とはいえ、この方法で保存することを選択しているデータが、照会する必要のないデータである場合は、選択した方法で保存する場合があります。

(一部のユーザーは、「将来どのような要件が追加されるかわからない」と言って、前の段落のステートメントに異議を唱えるでしょう。あなたの前に。)

38
Hammerite

一般に、プロジェクトの要件を満たしていれば、何でも防御できます。これは、人々があなたの決定に同意する、または擁護したいという意味ではありません...

一般に、この方法でデータを保存することは最適ではなく(たとえば、効率的なクエリを実行するのが難しくなります)、フォームのアイテムを変更するとメンテナンスの問題が発生する可能性があります。おそらく、中間点を見つけて、代わりにビットフラグのセットを表す整数を使用できたでしょうか?

17
bobbymcr

はい、私はそれが本当に悪いと言うでしょう。それは防御可能な選択ですが、それはそれを正しいまたは良いものにしません。

最初の正規形を壊します。

2番目の批判は、生の入力結果を検証やバインドを一切行わずにデータベースに直接入力すると、SQLインジェクション攻撃を受けやすくなるということです。

あなたが怠callingと呼んでいるのは、SQLの知識の不足です。時間をかけて適切に行い、学習の機会と見なすことをお勧めします。

または、そのままにして、SQLインジェクション攻撃の苦痛な教訓を学びます。

12
duffymo

複数値の列が必要でした。これはxmlフィールドとして実装できます

必要に応じて区切られたコンマに変換できます

Xqueryを使用してSQLサーバーでXMLリストをクエリする

Xmlフィールドであることにより、いくつかの懸念に対処できます。

CSVを使用:各値が正しいデータ型であることを保証できません:1,2,3、banana、5を防ぐ方法はありません

XMLの場合:タグ内の値を強制的に正しい型にすることができます


CSVの場合:外部キー制約を使用して値をルックアップテーブルにリンクすることはできません。参照整合性を強制する方法はありません。

XMLの場合:まだ問題


CSVの場合:一意性を強制できない:1,2,3,3,3,5を防ぐ方法はありません

XMLの場合:まだ問題


CSVの場合:リスト全体を取得しないとリストから値を削除できません。

XMLの場合:単一のアイテムを削除できます


CSVを使用:リスト内の特定の値を持つすべてのエンティティを検索するのは困難です。非効率的なテーブルスキャンを使用する必要があります。

XMLの場合:xmlフィールドにインデックスを付けることができます


CSVの場合:リスト内の要素をカウントすること、または他の集約クエリを実行することは困難です。**

XMLの場合:特に難しくない


CSVの場合:値が参照するルックアップテーブルに値を結合するのが難しい。**

XMLの場合:特に難しくない


CSVの場合:ソートされた順序でリストを取得するのは困難です。

XMLの場合:特に難しくない


CSVの場合:整数を文字列として保存するには、バイナリ整数を保存する場合の約2倍のスペースが必要です。

XMLの場合:ストレージはcsvよりもさらに悪い


CSVの場合:に加えて多くのコンマ文字。

XMLの場合:タグがコンマの代わりに使用されます


つまり、XMLを使用すると、区切りリストに関する問題のいくつかが回避され、必要に応じて区切りリストに変換できます。

7
James A Mohler

はい、それはisその悪い。私の見解では、リレーショナルデータベースの使用が気に入らない場合は、自分に合った選択肢を探すと、高度な機能を備えた興味深い「NOSQL」プロジェクトがたくさんあります。

6
Robin

さて、SQL ServerのNTEXT列でキーと値のペアのタブ区切りリストを4年以上使用してきましたが、機能します。クエリを作成する柔軟性は失われますが、一方で、キーと値のペアを永続化/永続化するライブラリがある場合、それは悪い考えではありません。

6
Raj

私はおそらく中間的な立場を取るでしょう。CSVの各フィールドをデータベースの個別の列にしますが、正規化についてはあまり心配しません(少なくとも今のところ)。ある時点で、正規化mightが興味深いものになりますが、すべてのデータが単一の列に押し込まれているため、データベースを使用してもまったく利点はありません。データを有意義に操作する前に、データを論理フィールド/列/呼び出したいものに分ける必要があります。

0
Jerry Coffin

固定数のブール型フィールドがある場合、それぞれにINT(1) NOT NULL(または存在する場合はBIT NOT NULL)またはCHAR (0)(nullable)を使用できます。 SETを使用することもできます(正確な構文は忘れています)。

0
Solomon Ucko