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


363

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

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

私の状況では、節約された時間とより単純なコードはそれだけの価値があると思いました。これは防御可能な設計の選択ですか、それとも最初から正規化する必要がありますか?

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


21
その場合、なぜデータベースに煩わされるのか、ファイルに保存するだけで十分です。
thavan

6
@thavanに同意しました。なぜ概念実証のためにデータを保存するのですか?証明が完了したら、データベースを正しく追加します。概念実証のために軽量で細かい作業を行っています。後でアンメイクする必要のあるものを作成しないでください。
ジェフデイビス

1
Postgresでは、コンマで区切られたリストよりも配列列を優先する必要があります。これにより、少なくとも適切なデータ型が保証され、区切り文字と実際のデータを区別するのに問題がなく、効率的にインデックスを作成できます。
a_horse_with_no_name

回答:


568

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

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

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

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

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


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


8
(任意のデータ型の)ARRAYだけではPostgreSQLをチェックし、例外を修正することができます。postgresql.org/docs/current/static/arrays.html(@Bill:グレート書籍、すべての開発者またはDBAのために読まなければならない)
フランクHeikens

4
+1ビルカーウィンすばらしい回答です。素敵な簡潔な箇条書き。それも素晴らしい本のようです。カバーも大好き+1 NullUserException。現在、MySQLデータベースのスキーマを設計して、フラットファイルのテキストベースのシステムを置き換えています。これまでにいくつかのジレンマに遭遇しました。したがって、この本は購入する価値があります。
therobyouknow 2012年

2
pragprog.comサイトも見栄えがいいです。すてきなスタイル、レイアウト、ユーザーフレンドリーなクリーン。これはかなり新しいものであるに違いない、私は過去に彼らの電子ブックを買うことができなかった。PS。私は彼らが作者と関係がないので働きません。私は良い製品、サービスを祝い、それを見たときに手助けするのが好きです。
therobyouknow 2012年

2
真面目に、私はあなたのリストに追加します:検索するのが難しい。「2」を含むすべてのレコードが必要だとします。もちろん、foobar = '2'だけを検索することはできません。他の値があった場合、検索が失敗するからです。「%2%」のようにfoobarを検索することはできません。12や28などで誤ったヒットが発生するためです。「%、2、%」のようにfoobarを検索することはできません。これは、2がリストの最初または最後の要素であり、コンマが1つしかないためです。
ジェイ

2
私はそれが推奨されないことを知っていますが、悪魔を支持することを支持します:一意性とデータ型を処理するUIがある場合(そうでない場合はエラーまたは誤動作します)、UIはそれをドロップして作成しますが、ドライバーテーブルがあります。値はそれらを一意にするために取得され、「%P%」のようなフィールドを使用できます。値はP、R、S、Tであり、カウントは関係ありません。また、ソートは関係ありません。uiに応じて、値を分割して[]にすることができます。たとえば、最も一般的なシナリオでは、ドライバーテーブルのリストのチェックボックスをチェックして、別のテーブルに移動する必要はありません。
jmcclure 2015

44

「一つの理由は怠惰だった」。

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

つまり、この方法で保存することを選択しているデータが、クエリを実行する必要がないデータである場合、選択した方法で保存する場合があります。

(一部のユーザーは、前の段落で「今後追加される要件を知ることは決してできない」と述べて、この意見に異議を唱えます。これらのユーザーは、見当違いであるか、または信念を述べています。時には、あなたの前にあります。)


外部キー制約を設定しない、単一のフィールドにリストを格納するなどの問題に直面すると、「私の設計はあなたの設計よりも柔軟です」と言う人がよくいます。私にとって、柔軟性(そのような場合)==規律なし==怠惰。
foresightyj 2015年

41

SOの質問には次のような多くの質問があります。

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

コンマ区切りのリストのもう1つの問題は、値の一貫性を確保することです。テキストを保存すると、入力ミスの可能性があります...

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


19

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

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


10

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

それは最初の通常形を壊します。

2つ目の批判は、検証やバインドをまったく行わずに、生の入力結果を直接データベースに入れると、SQLインジェクション攻撃にさらされてしまうことです。

あなたが怠惰と呼んでいるものとSQLの知識の欠如は、新生物が作られているものです。時間をかけて適切に実行し、学ぶ機会ととらえることをお勧めします。

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


19
この質問では、彼がSQLインジェクションに対して脆弱であることを示唆するものは何も見当たりません。SQLインジェクションとデータベースの正規化は直交するトピックであり、インジェクションに関する余談は質問とは無関係です。
Hammerite

5
@Paul:通りを横断する前に両方向を見ることができなかった場合、同じ態度で彼がバスにぶつかる可能性がありますが、警告はしていません。編集:私はあなたがこの答えの投稿者だと思っていました、私の間違い。
Hammerite

1
@Hammerite-バスへの外挿はばかげています。
duffymo

4
はい、それはばかげていることを意図していました。そのばかげていることは、私が主張している点を示しています。つまり、彼に警告する必要があると考える理由のない何かに対して彼に警告するのは意味がありません。
Hammerite

1
分かりました。バスについてのあなたの警告よりもはるかに多くの理由があったと思います。
duffymo

7

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


13
いいえ、それは恐ろしい考えです。なんとかそれをうまく使いこなすことができましたが、数分の開発時間のコストにより、コードのクエリパフォーマンス、柔軟性、および保守性がお粗末になりました。
ポールトンブリン

5
ポール、同意する。しかし、私が言ったように、私は特定の目的のために使用した場合、それはあなたが多くの種類のフォームを持っているデータ入力操作のためです。NHibernateを学習したのでデザインを修正しましたが、当時はASP.NETでフォームをデザインし、キー/値ペアのキーとしてテキストボックスIDを使用するために柔軟性が必要でした。
Raj

28
+1だけで反対投票に対抗します。アプリを4年間メンテナンスしている人にメンテナンスの問題について話すことは、少しおこがましいことです。sw開発には「恐ろしい」アイデアはほとんどありません。ほとんどの場合、それらは適用範囲が非常に限られている単なるアイデアです。制限について人々に警告することは理にかなっていますが、それを実行し、それを生き抜いた人々を懲らしめることは、私がなしにはできないあなたよりも崇高な態度として私を襲います。
Mark Brackett 2013

7

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

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

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

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

CSVの場合:各値が正しいデータ型であることを確認できません:1、2、3、バナナ、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を使用すると、区切りリストの問題のいくつかを回避でき、必要に応じて区切りリストに変換できます


6

はい、それ悪いです。私の見解では、リレーショナルデータベースを使用したくない場合は、より適切な代替案を探してください。非常に高度な機能を備えた興味深い「NOSQL」プロジェクトがたくさんあります。


0

私はおそらく中立的な立場を取るでしょう。CSVの各フィールドをデータベースの個別の列にしますが、正規化についてはあまり気にしません(少なくとも現時点では)。ある時点で、正規化興味深いものになる可能性がありますが、すべてのデータが1つの列に入れられているため、データベースを使用してもほとんどメリットがありません。意味のある操作を行う前に、データを論理フィールド/列/呼び出したいものに分割する必要があります。


フォームにはいくつかのフィールドが含まれていますが、これはフォームの一部にすぎません(質問では詳しく説明していません)。
マッドサイエンティスト

0

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


1
INT(1)4バイトかかります。これ(1)は無意味です。
リックジェームズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.