ユーザー定義フィールドを許可するのは悪い習慣ですか?


17

一般的に言えば、webappのデータベースにユーザーが作成したフィールドを許可することは悪い習慣と見なされますか?

たとえば、妻用のホームインベントリwebappを作成していますが、妻はさまざまなアイテム用に独自のフィールドを定義したいと考えています。私は彼女がアイテムのカテゴリを作成し、それらのカテゴリに「機能」を追加できるようにすることを計画していました。機能は、文字列として保存されたキー/値になります。たとえば、「オーディオCD」というカテゴリがある場合、「アーティスト」、「トラック」などの機能を追加できますが、「家具」などの別のカテゴリでは、「素材」などの機能を追加できます「(木材、プラスチックなど)。次に、任意のアイテムを1つ(または多数)のカテゴリに属し、それらの機能をアイテムに追加します。

これらの機能による検索には文字列の比較が必要であり、データの検証が必要ないなどの問題を見ることができます。アジャイル方法論に従って、新しいカテゴリと属性を考え出すことをお勧めします。私たちが行くように。私の例では、それは小さなユーザーベース(2人)であり、作成されるレコードの量は少ないので、それほど悪くはありません。

しかし一般的に言えば、人々は「現実の生活」でこのようなことをどのように処理しますか?


4
MongoDBのようなドキュメント指向データベースの使用を検討しましたか?スキーマとして機能するタイプごとにドキュメントを保存することもできますが、スキーマは編集することもできます(プロジェクトの規模が小さい場合は、おそらく手動で)。
アンディハント

@AndyBursh現在のpostgresの「楽しい」ビットの1つは、「json」データ型(link)です。このようなアプローチにより、ユーザーが指定したフィールドをそのデータ、er、document、erなどに保存し、残りのフィールドを適切なインデックスなどに使用できます。これはすべて使用方法に依存しますが、これが特定のアプリケーションでうまく機能するかどうかを判断するのは難しいです。しかし、注意する必要があります。

all:素晴らしい議論、すべての洞察に感謝します!@AndyBursh MongoDBについて聞いたことがありますが、実際に読んだことはありません。実験に別の家庭のプロジェクトのように聞こえる...
zako42

回答:


19

バグトラッカー、顧客リソース管理、および同様のビジネスツールでよく見られる「ユーザー定義フィールド」に到達し始めると、それらは無数のフィールドを持つテーブルに裏付けられていないということです(ある場合は、それ自身)。

代わりに、エンティティ属性値テーブルの設計と、有効な属性を管理する関連管理ツールがあります。

次の表を考慮してください。

  + -------------- +
  | こと|
  | -------------- |
  | id |
  | タイプ|
  | desc |
  | attr1 |
  | attr2 |
  | attr3 |
  | attr4 |
  | attr5 |
  + -------------- +

これは、いくつかの属性を追加した後です。代わりにattr1、それは読みふりartistまたはtracksまたはgenreまたは持っているものを何でも属性。そして、5の代わりに、50だったらどうなるでしょう。明らかに、それは管理不能です。また、新しいフィールドを処理するには、モデルの更新とアプリケーションの再デプロイメントが必要です。理想的ではありません。

ここで、次のテーブル構造を検討します。

  + -------------- + + --------------- + + ------------- +
  | こと| | thing_attr | | attr |
  | -------------- | | --------------- | | ------------- |
  | id | <--- + | thing_id(fk)| +> | id |
  | タイプ| | attr_id(fk)| +-+ | 名前|
  | desc | | 値| | |
  + -------------- + + --------------- + + ------------- +

基本的なフィールドがあります。さらに2つのテーブルがあります。属性を持つもの。各フィールドはattrテーブル内の行です。そしてthing_attrthingテーブルとテーブルに関連する外部キーのペアがありattrます。そして、これには値フィールドがあり、そのエンティティのフィールドの値が何であれ格納します。

また、実行時にattrテーブルを更新し、アプリケーション全体に大きな影響を与えることなく、新しいフィールドをその場で追加(または削除)できる構造ができました。

クエリは少し複雑で、検証も複雑になります(ファンキーなストアドプロシージャまたはすべてのクライアント側)。デザインのトレードオフ。

また、いつか移行を行う必要があり、アプリケーションに戻って、最初に配布したスキーマよりも半ダース以上の属性があることに気づいた場合も考えてください。これにより、エンティティ属性値テーブルが正しく使用された場合にクリーンになる可能性のあるい移行とアップグレードが行われます。(常にではありませんが、可能です。)


実行時にスキーマを変更するだけの欠点はありますか?ユーザーが何かに新しい属性が必要だと思う場合、動的にテーブルに列を追加しますか?

適切なフレーバーのnosqlデータベースを使用している場合は、おそらくこれを行うことができます(これに適したnosqlのフレーバーは、おそらく、上記のリレーショナルデータベースのEAVテーブルであるキーと値のストアです)面倒なし。 ただし、他の場所で詳細に説明されているnosqlのすべての妥協点があります。

代わりにリレーショナルデータベースで作業している場合は、スキーマが必要です。列を動的に追加するということは、次のことの一部が当てはまることを意味します。

  • メタデータベースプログラミングを行っています。素敵なORMでこの列をそのフィールドにきれいにマップできる代わりに、おそらく次のようなselect *ことをしてから、実際にデータが何であるかを調べるためにいくつかの複雑なコードを実行し(JavaのResultSetMetaDataを参照)、それをマップに保存します(または他のデータ型-しかし、コード内の素晴らしいフィールドではありません)。これにより、従来のアプローチで持っていたタイプとタイプの安全性がかなり失われます。
  • ORMを放棄した可能性があります。これは、システムに作業を任せる代わりに、すべてのコードに対して生のSQLを作成していることを意味します。
  • クリーンアップグレードの実行をあきらめました。顧客が、次のバージョンでも使用する1つの名前のフィールドを追加するとどうなりますか?マッチメイキングサイトではhasdate、タイムスタンプを保存するためのフィールドを追加するアップグレードは、マッチをhasdate成功させるためのブール値として既に定義されており、アップグレードが中断します。
  • クエリを中断する予約語を使用して、顧客がシステムを壊さないことを信頼しています...どこかで。
  • 1つのデータベースブランドに縛られています。異なるデータベースのDDLは異なります。データベースタイプは、この最も簡単な例です。 varchar2vs textなど。列を追加するコードはMySQLで機能しますが、Postgres、Oracle、またはSQL Serverでは機能しません。
  • 実際にデータを適切に追加することを顧客に信頼していますか?確かに、EAVは理想からはほど遠いですが、開発者が追加しなかったいくつかの恐ろしい不明瞭なテーブル名があり、間違ったタイプのインデックス(もしあれば)があり、必要なコードに制約が追加されていませんなどなど。
  • アプリケーションを実行しているユーザーにスキーマ変更権限を付与しました。Little Bobby Drop Tablesは、DDLではなくSQLに制限されている場合は使用できません(delete * from students代わりに使用できることを確認してください。ただし、実際にはデータベースを不適切な方法で台無しにすることはできません)。事故または悪意のあるアクティビティのいずれかによるスキーマアクセスで問題が発生する可能性のある数は急増しています。

これは本当に「やらないで」ということになります。これが本当に必要な場合は、EAVテーブル構造の既知のパターン、またはこの構造専用のデータベースを使用してください。テーブルに任意のフィールドを作成させないでください。頭痛はそれだけの価値はありません。


4
また、データベースを再発明しました。
user253751 14年

1
@immibisは、ユーザーがデータベースの残りの部分を変更したり、モデルを更新するために再展開を必要とせずに管理できるレイヤーを追加しました。

1
@immibis EAVは、長年、リレーショナルデータベースのサークルで活発に議論されてきました。理論的にはそれは不要ですが、実際には、それなしでは特定のことを行うことはできません。
ロスパターソン14年

1
NoSQLアプローチに移行する@ShivanDragon。ドキュメントストアはドキュメントを保存するだけで、スキーマを強制しません。そのため、フィールドの追加と削除、ドキュメントの解析は、データベース自体の範囲外です(そして、それに対応するためにモデルを作成しました)。これは、EAV構造に対するリレーショナルデータベースの侵害とはまったく異なる一連の侵害です。

1
関連チャットディスカッション:eavに代わるデータベース設計

5

これをうまくやるのは難しい。

計画しているような1回限りのアプリケーションの場合、もちろん、各フィールドに列を追加し、SQLコマンドラインを提供するよりも未熟なユーザーによるフィールド定義をより安全にするUIを提供できます。あるいは、恐ろしいEntity-Attribute-Valueパターンに従うこともできます。これは、この種の問題に対する古典的な、多少恐ろしい反応です。EAVフィールドを定義するためのUIの構築は、通常、データベース列の場合よりもはるかに複雑であり、クエリはかなりむずかしくなりますが、多数のフィールド(つまり、非常に疎なマトリックススキーマ)を取得する唯一の方法です仕事が終わった。


要約すると、小さなプロジェクト== KISS。地面にアジャイルダウン。
エンカイター14年

データベーステーブルの更新に関する問題は、必要なデータの量とインデックス(カスタムフィールドは多くの場合検索機能を必要とする)によっては、テーブル変更クエリに膨大な時間がかかる可能性があることです。簡単に言えば、MySQLやその他のリレーショナルデータベースは、この種の要件に適したメディアではないということです。
オッドマン

0

私は最近、似たようなクロスをしました。

2つのテーブルを作成しました。

1: table Objects 
    Id , name, type

彼はすべてあなたのオブジェクトです。Uセット名。

そして、このオブジェクトのタイプ:-私にとって利用可能なタイプはinventory、inventory_item、officeでした。

そして、通常のセットアップはnアイテムが子またはインベントリであり、これもオフィスの子であり、オブジェクトを互いに結合するために結合テーブルを使用しました

2 table settings 
     organization_Id , title, value , type

設定テーブルには、その特定のオブジェクトタイプのすべてのフィールド名、および値の値が含まれます。

オフィスのプロパティの例

場所、電話、労働時間

アイテムについて

  • 価格
  • バーコード

など、これらのプロパティはすべてモデル化され、個別の行として設定テーブルに保存されます(ただし、同じフィールドに複数の行が挿入されないようにするには、置換ではなく挿入を使用します)

そのため、オフィスが必要になったときは、設定object_I'd(要求されたオブジェクト)のすべての関係と設定を簡単に読み込みます。

その後、すべての行を設定からピボットし、それで終わりです。

また、設定をインベントリ内のアイテム(グローバルではない)に固有にする場合は、object_I'd = I'dをobject_objectsリレーションテーブルから設定し、settings.type = relation_settingを設定します。

私がラップトップに着いたときに答えを再フォーマットしようとすることの意味を理解してほしい


2
プロのヒント-携帯電話からこのフォーラムに投稿しないでください。自動修正により、投稿の一部が読めなくなります。
BobDalgleish

ハハ良い観察:)
ザラボーザ14年

0

ユーザー定義フィールドを許可するのは悪い習慣ですか?

いいえ、悪い習慣ではありません。それは非常に一般的です。オブジェクト指向では、これは継承と呼ばれます。基本クラスのinventoryItemと、継承された2つのクラスAudioCDおよびFurnitureがあります。

しかし一般的に言えば、人々は「現実の生活」でこのようなことをどのように処理しますか?

inventoryItem、AudioCD、家具をデータベースに保存する方法を決定する必要があります。

簡単なクエリが最も重要であり、db-space / normalisationが重要でない場合は、「table-per-hierarchy」スキーマを実装します。

スペース/正規化が最も重要であり、より複雑なクエリで問題ない場合は、「table-per-type」スキーマを実装します。

詳細については、dotnet table-per-type-vs-table-per-hierarchy-inheritance またはjava hibernate inheritanceを参照してください


これで問題が解決するかどうかはわかりません。ユーザーは新しいクラスを作成するためにコードを変更していません
コリンD
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.