PostgreSQLに一対多または多対多の関係を格納する最良の方法は何ですか?

私は現在、オープンソースのチャット(AJAXチャット)を別のプロジェクトに統合しています。さて、デフォルトでチャットは有効なユーザーと有効なチャンネルをファイルから取得するだけですが、ユーザーが絶えず変化するデータベースを使用している場合、これは理想的ではありません。

ですから、チャットでユーザーとチャンネルの情報がデータベースから直接読み込まれるようにしたいと思います。私はデザインが次のようになるべきだと考えていました(あなたが違って感じたら、教えてください):

  • チャットチャンネル(一般公開、マーケティングなど)があります。
  • 次に、チャネルに割り当てられたグループがあります(PRチーム1、IT担当者など)
  • 次に、グループに参加しているユーザーがいて、場合によってはチャンネルに直接割り当てられます。

私はこれらのテーブルを次のように実装することを考えていました:

チャネルテーブル:

|----|Channel_Name||Channel_ID||Groups_Assigned||Users_Assigned|----|  
|----|---Public---||-----0----||---1,2,3,4,5---||-----3,4------|----|  
.  
.  
.etc...

注:グループに割り当てられたテーブルには、チャネルに割り当てられたグループのグループIDが含まれますが、割り当てられたユーザーには、割り当てられたグループに属していないユーザーのIDが含まれます。

グループテーブル:

|----|Group_Name||Group_ID||Users_Assigned|----|  
|----|---Team1--||----0---||------5,10----|----|  
.  
.  
.etc...  

ひどく描かれたテーブルを残念に思います。

今、上記の実装では、ユーザーがログインすると、プログラムはユーザーID(ユーザー表から)を取得し、ユーザーIDを含むすべてのグループのグループ表を検索し、最後にチャネル表を検索します(ユーザーが属している)グループまたはユーザーが直接割り当てられているチャネルのいずれかを含むすべてのチャネルについて、

私のアイデアは可能ですが、それはちょっと、うーん、非効率です。私は割り当てられたid(グループとユーザの両方)を 1,2,3 .... の形式で格納しなければならないので、PHPの explode()または文字列を検索できるPostgreSQLの他の関数です。私はおそらく、グループの配列を格納しているし、一度に1行ずつ、それらをすべて循環している、これは私にとっては本当に遅いようです。

または、ユーザーごとにブール値の列を作成することもできますが、結果的に列が多すぎるため、ユーザーが作成されるたびに新しい列を作成する必要はありません。

だから、どうやってこれをやるの?そして、あなたが私の最初のアイデアに同意したら、実際にコードを書く方法を理解することができますか?

あなたの時間をありがとう、良い一日を。

4
あなたの直感は正しいです、それは良いデザインではありません。これを読んでくださいは、データベース内で列挙されたコンマで区切られたリストを格納していますが、これは悪い stackoverflow .com/questions/3653462 /…
追加された 著者 ypercubeᵀᴹ,

3 答え

Yes, it's inefficient to store comma-separated strings of numbers and to try to search your database for a given number. For more on this, see my answer to Is storing a comma separated list in a database column really that bad?

代わりに、交差テーブルを使用して、ユーザーとグループ間、およびグループとチャネル間の多対多の関係を保存する必要があります。次に、索引による検索が有効になり、結合を使用してグループまたはチャネル表に戻ることができます。

6
追加された

私は 1,2,3,4,5 の値の代わりにもう一つテーブルを読むのは難しいので、 channels テーブルから Groups_Assigned を削除し、別のテーブルに1から複数の形式で入力します。

Channel_id  Group_id
----------  --------
0           1
0           2
0           3
0           4
0           5

私はここでこのテーブルに参加するグループとして別のテーブルを作成し、それぞれの group_id の情報を保持します。次に、必要に応じてこのデザインを読むことができるクエリを書くことの問題です。

2
追加された
私は例としてgroups_assignedを使用しましたが、user_assignedでも同じロジックが最適です。 1つの 'user_assigned'テーブルにchannelとgroupの両方のuser_assignedポイントを持つことは可能です
追加された 著者 Twelfth,
Heh、ばかではない...私は同じことをやろうとした。私はこのようなフォーラムから別のことを学んだと信じています。あなたが何かの線形からDB設計に来るなら...配列はSQLを吸うことを覚えています。 1対多の関係は、コードの容易さとパフォーマンスの観点から、この形式で読みやすくなります
追加された 著者 Twelfth,
ありがとう、私はおそらくこのような何かを終わらせるでしょう。私はこの解決策を考えていましたが、何らかの理由で私の愚かな脳がそれを解消しました。
追加された 著者 zermy,

1つの可能な解決策:

Channel
------------
Channel_Id
Channel_Name
PRIMARY KEY (Channel_Id)

Person Grouping (これは User Group スーパータイプエンティティのサブタイプと考えてください。これは後で Assignment テーブルを1つだけ持つのに役立ちます。

Entity
------------
Entity_Id
PRIMARY KEY (Entity_Id)

Person  --- ( User )
------------
Person_Id
Person_Name
--- other data about persons/users
PRIMARY KEY (Person_Id)
FOREIGN KEY (Person_Id)
  REFERENCES Entity(Entity_Id)

Grouping   --- ( Group )
------------
Grouping_Id
Grouping_Name
--- other data about groups
PRIMARY KEY (Grouping_Id)
FOREIGN KEY (Grouping_Id)
  REFERENCES Entity(Entity_Id)

これは Person - Grouping の関連付けに使用されます:

Belongs --- ( Person Belongs In Grouping )
------------
Person_Id
Grouping_Id
PRIMARY KEY (Person_Id, Grouping_Id)
FOREIGN KEY (Person_Id)
  REFERENCES Person(Person_Id)
FOREIGN KEY (Grouping_Id)
  REFERENCES Grouping(Grouping_Id)

そして、チャンネルへの割り当ての関連テーブル

Assignment ( Entity is Assigned to Channel )
------------
Entity_Id
Channel_Id
PRIMARY KEY (Entity_Id, Channel_Id)
FOREIGN KEY (Entity_Id)
  REFERENCES Entity(Entity_Id)
FOREIGN KEY (Channel_Id)
  REFERENCES Channel(Channel_Id)

もちろん、 Entity テーブルを削除して、 Person to Channel Group to Channel の2つの関連テーブルを持つことができます。

1
追加された
@ypercube:あなたを正しく理解していれば、ユーザーまたはグループのテーブルの既存の主キー列名または主キー値を変更する必要はありません。ユーザー、グループ、およびエンティティで追加の列が必要です ID番号は両方のサブタイプテーブルに現れることはできませんが、あなたはそれを行う必要があります。これで、ベーステーブルの名前を変更して、アプリケーションコードからこれらの変更をすべて隠すための更新可能なビューを構築できると思いますが、テストなしでは100%確実ではありません。
追加された 著者 Mike Sherrill 'Cat Recall&,
あなたのユーザテーブルを参照するテーブルがたくさんある場合、それらの参照を変更する必要はありません。すでにユーザーとグループの表があり、Entity表を追加する場合は、2つの表のいずれかで主キーの値を変更する必要があるため、 Users.User_Id または Groups.Group_Id およびそれらの値をすべて参照してください)。
追加された 著者 ypercubeᵀᴹ,
さらに、ユーザーとグループに INSERT プロシージャを調整する必要があります(最初にEntityに挿入し、新しいPKを使用してユーザーまたはグループに挿入します)。
追加された 著者 ypercubeᵀᴹ,
その場合、 User-Channel の関連付けと Group-Channel に1つずつ、2つの交差テーブルを使用する必要があります。
追加された 著者 ypercubeᵀᴹ,
@Catcall:はい、そうですが、一意のキーを追加し、既存の主キーを改ざんすることなく、同じ参照整合性を達成できます。私はそれを考えなかった。それは最良のものではありませんが、すでにデータがある場合は、テーブルの最小限の変更と既存の(PK)データの変更を必要としない唯一のソリューションと思います。
追加された 著者 ypercubeᵀᴹ,
ああ、これはすてきな解決策ですが、私はすでに人口が多いユーザーテーブルを持っていますので、私は質問しなければならないと感じています。 "既にユーザーテーブルと300以上の他のテーブルを持っているとき(私は、最初にエンティティを持っていなければならないことを知っていますが、それを参照してください)他にも、私は交差テーブルを使うべきであるというあなたと他の人の答えに同意します。
追加された 著者 zermy,
まあ、 INSERTのを忘れてしまって、それを変更するのは大変な作業ですが、ソリューションのおかげで、何かをゼロから作成すると思います。
追加された 著者 zermy,
うん、私はするつもりです。
追加された 著者 zermy,
PHP - 日本のコミュニティ [ja]
PHP - 日本のコミュニティ [ja]
4 参加者の

このグループではPHPについて話します。 パートナー:kotaeta.com