web-dev-qa-db-fra.com

Alternatives à stocker un enregistrement avec exactement N touches étrangères multiples de la même table étrangère, où les relations ne peuvent pas être répétées

Dis qu'il y a des entités appelées singulars et entités appelées relationships.

Il faut exactement deux singulars pour constituer une entité relationships.

C'est paire de singuliers ne peut pas être répété ailleurs dans relationships, dans n'importe quel ordre.

Une façon de modéliser cela pourrait être de cette façon:

+----------------+
|relationships   |         +----------+
+----------------+         |singulars |
|id              |         +----------+
|singular_id_1   <---------+id        |
|singular_id_2   <---+     |attribute1|
|pair_description|         |attribute2|
|pair_date       |         |          |
|                |         +----------+
+----------------+

Avec ce motif, il devient nécessaire de vérifier les deux champs de clé étrangers dans relationships pour l'existence de singulars, qui pourrait être de chaque côté. L'ordre n'a pas d'importance, mais il est défini dans le schéma ... afin que les requêtes se retrouvent avec un certain nombre de AND/OR groupes et cas.

L'expansion sur cette approche pourrait être de stocker deux enregistrements de chaque paire, avec le singular_id_[n] échangé des deux côtés. Bien que cela résout certaines complexités interrogées, cela introduirait des complexités supplémentaires pour le rendre infaisable.

L'utilisation d'une table intermédiaire semble être une solution potentielle:

+----------------+       +-----------------------+
|relationships   |       |singulars_relationships|        +----------+
+----------------+       +-----------------------+        |singulars |
|id              <-------+relationship_id        |        +----------+
|pair_description|       |singular_id            +-------->id        |
|pair_date       |       |                       |        |attribute1|
|                |       +-----------------------+        |attribute2|
+----------------+                                        |          |
                                                          +----------+

Les enregistrements peuvent donc finir par quelque chose comme ça:

+----------------------------------+
|relationships                     |
+----------------------------------+
|id   pair_description   pair_date |
+----------------------------------+
|1    Fizz buzz blitz    2022-02-20|
|2    Blitz buzz fizz    2022-02-22|
+----------------------------------+

+----------------------------------+
|singulars_relationships           |
+----------------------------------+
|relationship_id   singular_id     |
+----------------------------------+
|1                 1               |
|1                 2               |
|2                 3               |
|2                 4               |
+----------------------------------+

+-----------------------------+
|singulars                    |
+-----------------------------+
|id   attribute1   attribute2 |
+-----------------------------+
|1    Fizz         Blitz      |
|2    Buzz         Foo        |
|3    Bar          World      |
|4    Blorg        Hello      |
+-----------------------------+

Là, singulars_relationships est l'endroit où les paires sont définies. Si un singular_id existe là-bas, c'est déjà dans une paire. Un problème qui peut survenir avec ce modèle pourrait être que trois ou plus singular_id s pourrait finir par associé à un relationship_id, et la contrainte "exactement n" serait alors compromise.

Y a-t-il des termes officiels pour ce type de scénario? Et autre théorie et alternatives?

6
groovenectar

Y a-t-il des termes officiels pour ce type de scénario?

Oui. Ceci est une Relation symétrique . Et "relation" ici a la même signification que dans "Base de données relationnelle". Un RDBMS est un système de gestion de base de données conçu autour de la conservation des relations. Cependant, les RDBMS n'ont pas de manière native de stocker des relations symétriques. Vous devez soit stocker les deux tuples, par exemple (A, B) et (B, A) comme rangées séparées, ou vous devez utiliser une sorte de convention pour stocker un seul tuple. Une approche commune consiste à utiliser une contrainte de contrôle sur les FKS.

par exemple

check (singular_id_1 < singular_id_2)

En supposant que la relation soit anti-réflexive .

Pour un commentaire, je vais donc ajouter cela comme un complément à David's solution. Même si la question était de nature théorique, il peut être intéressant de voir comment cela peut être mis en œuvre dans la vie réelle.

Dans certaines situations, la propriété anti-réflexive et la propriété symétrique est trop forte, mais nous voulons toujours que la propriété "unicité" soit tenue. Un modèle commun est d'utiliser BEFORE TRIGGERS Pour rendre la relation symétrique sans laisser les utilisateurs devoir être au courant de la propriété symétrique:

CREATE TRIGGER trigger1
BEFORE INSERT ON t
REFERENCING NEW AS N
FOR EACH ROW
    SET (N.a, N.b) = (LEAST(N.a, N.b), GREATEST(N.a, N.b));

Propriétés dans le tableau:

CHECK(a<=b);
UNIQUE(a,b);

Une variante de ce thème consiste à utiliser des colonnes générées

CREATE TABLE t 
( a int not null
, b int not null
, least_a_b generated always as ( least(a,b) )
, greatest_a_b generated always as ( greatest(a,b))
, unique (least_a_b, greatest_a_b)
);

Les colonnes générées peuvent également être cachées:

least_a_b generated always as ( least(a,b) ) IMPLICITLY HIDDEN

Des exemples proviennent de DB2, mais des fonctionnalités similaires devraient exister pour d'autres fournisseurs.

7
Lennart