Comment modéliser un type d'entité qui peut avoir différents ensembles d'attributs?

11

J'ai du mal à recréer une base de données avec une relation un-à-plusieurs (1: M) entre les utilisateurs et les éléments .

C'est assez simple, oui; cependant, chaque article appartient à une certaine catégorie (par exemple, une voiture , un bateau ou un avion ), et chaque catégorie a un nombre particulier d'attributs, par exemple:

Car structure:

+----+--------------+--------------+
| PK | Attribute #1 | Attribute #2 |
+----+--------------+--------------+

Boat structure:

+----+--------------+--------------+--------------+
| PK | Attribute #1 | Attribute #2 | Attribute #3 |
+----+--------------+--------------+--------------+

Plane structure:

+----+--------------+--------------+--------------+--------------+
| PK | Attribute #1 | Attribute #2 | Attribute #3 | Attribute #4 |
+----+--------------+--------------+--------------+--------------+

En raison de cette diversité dans le nombre d'attributs (colonnes), j'ai d'abord pensé que ce serait une bonne idée de créer une table distincte pour chaque catégorie , donc j'éviterais plusieurs NULLs et ainsi mieux utiliser l'indexation.

Bien que cela paraisse bien au début, je n'ai pas trouvé de moyen de créer la relation entre les articles et les catégories via la base de données car, au moins dans ma modeste expérience en tant qu'administrateur de base de données, lors de la création de clés étrangères, j'informe explicitement une base de données le nom et la colonne de la table.

En fin de compte, je voudrais une structure solide pour stocker toutes les données, tout en ayant tous les moyens de répertorier tous les attributs de tous les éléments qu'un utilisateur peut avoir avec une seule requête.

Je pouvais coder en dur des requêtes dynamiques avec le langage côté serveur , mais je pense que c'est faux et pas très optimal.

Information additionnelle

Voici mes réponses aux commentaires de MDCCL:

1. Combien de catégories d'articles d'intérêt existe-t-il dans votre contexte commercial, trois (c.-à-d. Voitures , bateaux et avions ) ou plus?

En fait, c'est très simple: il n'y a que cinq catégories au total.

2. Le même élément appartiendra-t-il toujours au même utilisateur (c'est-à-dire qu'une fois qu'un élément donné a été «attribué» à un certain utilisateur, il ne peut pas être modifié)?

Non, ils pourraient changer. Dans le scénario fictif de la question, ce serait comme si l' utilisateur A vend l'article n ° 1 à l'utilisateur B , donc la propriété doit être reflétée.

3. Y a-t-il des attributs qui sont partagés par certaines ou toutes les catégories ?

Non partagé mais, de mémoire, je peux dire qu'au moins trois attributs sont présents dans toutes les catégories .

4. Y a-t-il une chance que la cardinalité de la relation entre l' utilisateur et l' élément soit plusieurs-à-plusieurs (M: N) au lieu d'un-à-plusieurs (1: M)? Par exemple, dans le cas des règles commerciales suivantes: A User owns zero-one-or-many ItemsetAn Item is owned by one-to-many Users

Non, car les éléments décriraient un objet physique. Les utilisateurs en auront une copie virtuelle, chacune identifiée par un GUID unique v4

5. Concernant votre réponse suivante à l'un des commentaires de la question:

"Dans le scénario fictif de la question, ce serait comme si l' utilisateur A vend l'article n ° 1 à l'utilisateur B , donc la propriété doit être reflétée."

Il semble que vous envisagiez de suivre l' évolution de la propriété des articles , pour ainsi dire. De cette façon, quels attributs aimeriez-vous stocker sur un tel phénomène? Seule la modification de l'attribut qui indique l' utilisateur spécifique qui est le propriétaire d'un article spécifique ?

Non, pas vraiment. La propriété peut changer, mais je n'ai pas besoin de garder une trace du propriétaire précédent .

user5613506
la source

Réponses:

18

Selon votre description de l'environnement commercial considéré, il existe une structure de sous-type de supertype qui englobe l' élément —le supertype— et chacune de ses catégories , c'est-à-dire la voiture , le bateau et l' avion (ainsi que deux autres qui n'ont pas été révélées) - les sous-types—.

Je détaillerai ci-dessous la méthode que je suivrais pour gérer un tel scénario.

Règles métier

Afin de commencer à délimiter le schéma conceptuel pertinent , certaines des règles commerciales les plus importantes déterminées à ce jour (en limitant l'analyse aux trois catégories divulguées uniquement, pour que les choses soient aussi brèves que possible) peuvent être formulées comme suit:

  • Un utilisateur possède zéro ou un ou plusieurs éléments
  • Un article appartient à un seul utilisateur à un instant donné
  • Un article peut appartenir à un ou plusieurs utilisateurs à des moments différents
  • Un article est classé par exactement une catégorie
  • Un article est, à tout moment,
    • soit une voiture
    • ou un bateau
    • ou un avion

Diagramme illustratif IDEF1X

La figure 1 affiche un diagramme IDEF1X 1 que j'ai créé pour regrouper les formulations précédentes ainsi que d'autres règles métier qui semblent pertinentes:

Figure 1 - Structure des sous-types de sous-type d'élément et de catégories

Supertype

D'une part, Item , le supertype, présente les propriétés ou attributs communs à toutes les Catégories , c.-à-d.

  • CategoryCode —specified as a FOREIGN KEY (FK) qui fait référence à Category.CategoryCode et fonctionne comme un discriminateur de sous-type , c'est-à-dire qu'il indique la catégorie exacte de sous-type avec laquelle un élément donné doit être connecté—,
  • OwnerId —distitué comme un FK qui pointe vers User.UserId , mais je lui ai attribué un nom de rôle 2 afin de refléter plus précisément ses implications spéciales—,
  • Foo ,
  • Bar ,
  • Baz et
  • CreatedDateTime .

Sous-types

D'un autre côté, les propriétés qui se rapportent à chaque catégorie particulière , c.-à-d.

  • Qux et Corge ;
  • Grault , Garply et Plugh ;
  • Xyzzy , Thud , Wibble et Flob ;

sont affichés dans la boîte de sous-type correspondante.

Identifiants

Ensuite, la clé PRIMARY Item.ItemId (PK) a migré 3 vers les sous-types avec des noms de rôle différents, c'est-à-dire

  • CarId ,
  • BoatId et
  • PlaneId .

Associations mutuellement exclusives

Comme illustré, il existe une association ou une relation de cardinalité un à un (1: 1) entre (a) chaque occurrence de supertype et (b) son instance de sous-type complémentaire.

Le symbole de sous-type exclusif décrit le fait que les sous-types s'excluent mutuellement, c'est-à-dire qu'une occurrence d' article concrète peut être complétée par une seule instance de sous-type uniquement: soit une voiture , soit un avion , soit un bateau (jamais par deux ou plus).

, J'ai utilisé des noms d'espace réservé classiques pour autoriser certaines propriétés de type d'entité, car leurs dénominations réelles n'ont pas été fournies dans la question.

Disposition de niveau logique expositoire

Par conséquent, afin de discuter d'une conception logique expositoire, j'ai dérivé les instructions SQL-DDL suivantes basées sur le diagramme IDEF1X affiché et décrit ci-dessus:

-- You should determine which are the most fitting 
-- data types and sizes for all your table columns 
-- depending on your business context characteristics.

-- Also, you should make accurate tests to define the 
-- most convenient INDEX strategies based on the exact 
-- data manipulation tendencies of your business context.

-- As one would expect, you are free to utilize 
-- your preferred (or required) naming conventions. 

CREATE TABLE UserProfile (
    UserId          INT      NOT NULL,
    FirstName       CHAR(30) NOT NULL,
    LastName        CHAR(30) NOT NULL,
    BirthDate       DATE     NOT NULL,
    GenderCode      CHAR(3)  NOT NULL,
    Username        CHAR(20) NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT UserProfile_PK  PRIMARY KEY (UserId),
    CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
        FirstName,
        LastName,
        GenderCode,
        BirthDate
    ),
    CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- ALTERNATE KEY.
);

CREATE TABLE Category (
    CategoryCode     CHAR(1)  NOT NULL, -- Meant to contain meaningful, short and stable values, e.g.; 'C' for 'Car'; 'B' for 'Boat'; 'P' for 'Plane'.
    Name             CHAR(30) NOT NULL,
    --
    CONSTRAINT Category_PK PRIMARY KEY (CategoryCode),
    CONSTRAINT Category_AK UNIQUE      (Name) -- ALTERNATE KEY.
);

CREATE TABLE Item ( -- Stands for the supertype.
    ItemId           INT      NOT NULL,
    OwnerId          INT      NOT NULL,
    CategoryCode     CHAR(1)  NOT NULL, -- Denotes the subtype discriminator.
    Foo              CHAR(30) NOT NULL,
    Bar              CHAR(30) NOT NULL,
    Baz              CHAR(30) NOT NULL,  
    CreatedDateTime  DATETIME NOT NULL,
    --
    CONSTRAINT Item_PK             PRIMARY KEY (ItemId),
    CONSTRAINT Item_to_Category_FK FOREIGN KEY (CategoryCode)
        REFERENCES Category    (CategoryCode),
    CONSTRAINT Item_to_User_FK     FOREIGN KEY (OwnerId)
        REFERENCES UserProfile (UserId)  
);

CREATE TABLE Car ( -- Represents one of the subtypes.
    CarId INT      NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    Qux   CHAR(30) NOT NULL,
    Corge CHAR(30) NOT NULL,   
    --
    CONSTRAINT Car_PK         PRIMARY KEY (CarId),
    CONSTRAINT Car_to_Item_FK FOREIGN KEY (CarId)
        REFERENCES Item (ItemId)  
);

CREATE TABLE Boat ( -- Stands for one of the subtypes.
    BoatId INT      NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    Grault CHAR(30) NOT NULL,
    Garply CHAR(30) NOT NULL,   
    Plugh  CHAR(30) NOT NULL, 
    --
    CONSTRAINT Boat_PK         PRIMARY KEY (BoatId),
    CONSTRAINT Boat_to_Item_FK FOREIGN KEY (BoatId)
        REFERENCES Item (ItemId)  
);

CREATE TABLE Plane ( -- Denotes one of the subtypes.
    PlaneId INT      NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    Xyzzy   CHAR(30) NOT NULL,
    Thud    CHAR(30) NOT NULL,  
    Wibble  CHAR(30) NOT NULL,
    Flob    CHAR(30) NOT NULL,  
    --
    CONSTRAINT Plane_PK         PRIMARY KEY (PlaneId),
    CONSTRAINT Plane_to_Item_PK FOREIGN KEY (PlaneId)
        REFERENCES Item (ItemId)  
);

Comme démontré, le type de superentité et chacun des types de sous-entité sont représentés par la table de base correspondante .

Les colonnes CarId, BoatIdet PlaneId, comme les contraintes des tables PKs appropriées, aide à représenter l'un à une association niveau conceptuel par le biais de FK contraintes § ce point à la ItemIdcolonne, qui est contrainte comme la pharmacocinétique de la Itemtable. Cela signifie que, dans une «paire» réelle, les lignes de supertype et de sous-type sont identifiées par la même valeur PK; il est donc plus que opportun de mentionner que

  • (a) attacher une colonne supplémentaire pour contenir les valeurs de substitution contrôlées par le système à (b) les tableaux représentant les sous-types est (c) entièrement superflu .

§ Afin d'éviter les problèmes et les erreurs concernant les définitions de contraintes KEY (en particulier ÉTRANGÈRES) - situation que vous avez mentionnée dans les commentaires -, il est très important de prendre en compte la dépendance à l' existence qui existe entre les différentes tables à portée de main, comme illustré dans l'ordre de déclaration des tables dans la structure DDL expositoire, que j'ai également fourni dans ce SQL Fiddle .

Par exemple, ajouter une colonne supplémentaire avec la propriété AUTO_INCREMENT à une table d'une base de données construite sur MySQL.

Considérations d'intégrité et de cohérence

Il est essentiel de souligner que, dans votre environnement commercial, vous devez (1) vous assurer que chaque ligne de «supertype» est à tout moment complétée par son homologue «sous-type» correspondante et, à son tour, (2) garantir que ladite La ligne "sous-type" est compatible avec la valeur contenue dans la colonne "discriminateur" de la ligne "supertype".

Il serait très élégant d'appliquer de telles circonstances de manière déclarative mais, malheureusement, aucune des principales plates-formes SQL n'a fourni les mécanismes appropriés pour le faire, à ma connaissance. Par conséquent, le recours au code procédural dans ACID TRANSACTIONS est assez pratique pour que ces conditions soient toujours remplies dans votre base de données. Une autre option consisterait à utiliser TRIGGERS, mais ils ont tendance à rendre les choses désordonnées, pour ainsi dire.

Déclarer des vues utiles

Ayant une conception logique comme celle expliquée ci-dessus, il serait très pratique de créer une ou plusieurs vues, c'est-à-dire des tables dérivées qui comprennent des colonnes qui appartiennent à deux ou plusieurs des tables de base pertinentes . De cette façon, vous pouvez, par exemple, sélectionner directement à partir de ces vues sans avoir à écrire toutes les jointures à chaque fois que vous devez récupérer des informations «combinées».

Exemples de données

À cet égard, disons que les tables de base sont «remplies» avec les exemples de données ci-dessous:

--

INSERT INTO UserProfile 
    (UserId, FirstName, LastName, BirthDate, GenderCode, Username, CreatedDateTime)
VALUES
    (1, 'Edgar', 'Codd', '1923-08-19', 'M', 'ted.codd', CURDATE()),
    (2, 'Michelangelo', 'Buonarroti', '1475-03-06', 'M', 'michelangelo', CURDATE()),
    (3, 'Diego', 'Velázquez', '1599-06-06', 'M', 'd.velazquez', CURDATE());

INSERT INTO Category 
    (CategoryCode, Name)
VALUES
    ('C', 'Car'), ('B', 'Boat'), ('P', 'Plane');

-- 1. ‘Full’ Car INSERTion

-- 1.1 
INSERT INTO Item
    (ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
    (1, 1, 'C', 'This datum', 'That datum', 'Other datum', CURDATE());

 -- 1.2
INSERT INTO Car
    (CarId, Qux, Corge)
VALUES
    (1, 'Fantastic Car', 'Powerful engine pre-update!');

-- 2. ‘Full’ Boat INSERTion

-- 2.1
INSERT INTO Item
  (ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
  (2, 2, 'B', 'This datum', 'That datum', 'Other datum', CURDATE());

-- 2.2
INSERT INTO Boat
    (BoatId, Grault, Garply, Plugh)
VALUES
    (2, 'Excellent boat', 'Use it to sail', 'Everyday!');

-- 3 ‘Full’ Plane INSERTion

-- 3.1
INSERT INTO Item
  (ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
  (3, 3, 'P', 'This datum', 'That datum', 'Other datum', CURDATE());

-- 3.2
INSERT INTO Plane
    (PlaneId, Xyzzy, Thud, Wibble, Flob)
VALUES
    (3, 'Extraordinary plane', 'Traverses the sky', 'Free', 'Like a bird!');

--

Ensuite, une vue avantageuse est une vue qui rassemble des colonnes de Item, Caret UserProfile:

--

CREATE VIEW CarAndOwner AS
    SELECT C.CarId,
           I.Foo,
           I.Bar,
           I.Baz,
           C.Qux,
           C.Corge,           
           U.FirstName AS OwnerFirstName,
           U.LastName  AS OwnerLastName
        FROM Item I
        JOIN Car C
          ON C.CarId = I.ItemId
        JOIN UserProfile U
          ON U.UserId = I.OwnerId;

--

Naturellement, une approche similaire peut être suivie afin que vous puissiez également sélectionner le «complet» Boatet les Planeinformations directement à partir d' une seule table (une dérivée, dans ces cas).

Après que -si vous ne me dérange pas de la présence de marques de NULL dans le résultat ensembles- avec la définition VIEW suivante, vous pouvez, par exemple, « Collect » colonnes des tables Item, Car, Boat, Planeet UserProfile:

--

CREATE VIEW FullItemAndOwner AS
    SELECT I.ItemId,
           I.Foo, -- Common to all Categories.
           I.Bar, -- Common to all Categories.
           I.Baz, -- Common to all Categories.
          IC.Name      AS Category,
           C.Qux,    -- Applies to Cars only.
           C.Corge,  -- Applies to Cars only.
           --
           B.Grault, -- Applies to Boats only.
           B.Garply, -- Applies to Boats only.
           B.Plugh,  -- Applies to Boats only.
           --
           P.Xyzzy,  -- Applies to Planes only.
           P.Thud,   -- Applies to Planes only.
           P.Wibble, -- Applies to Planes only.
           P.Flob,   -- Applies to Planes only.
           U.FirstName AS OwnerFirstName,
           U.LastName  AS OwnerLastName
        FROM Item I
        JOIN Category IC
          ON I.CategoryCode = IC.CategoryCode
   LEFT JOIN Car C
          ON C.CarId = I.ItemId
   LEFT JOIN Boat B
          ON B.BoatId = I.ItemId
   LEFT JOIN Plane P
          ON P.PlaneId = I.ItemId               
        JOIN UserProfile U
          ON U.UserId = I.OwnerId;

--

Le code des vues présentées ici n'est qu'illustratif. Bien sûr, faire des tests et des modifications pourrait aider à accélérer l'exécution (physique) des requêtes en cours. En outre, vous devrez peut-être supprimer ou ajouter des colonnes auxdites vues en fonction des besoins de l'entreprise.

Les exemples de données et toutes les définitions de vues sont incorporés dans ce SQL Fiddle afin qu'ils puissent être observés «en action».

Manipulation des données: code de programme (s) d'application et alias de colonne

L'utilisation du code des programmes d'application (si c'est ce que vous entendez par «code spécifique côté serveur») et des alias de colonne sont d'autres points importants que vous avez soulevés dans les commentaires suivants:

  • J'ai réussi à contourner un problème [JOIN] avec du code spécifique côté serveur, mais je ne veux vraiment pas que cela -et- l'ajout d'alias à toutes les colonnes soit "stressant".

  • Très bien expliqué, merci beaucoup. Cependant, comme je le soupçonnais, je devrai manipuler l'ensemble de résultats lors de la liste de toutes les données en raison des similitudes avec certaines colonnes, car je ne veux pas utiliser plusieurs alias pour garder la déclaration plus propre.

Il est opportun d'indiquer que l'utilisation du code de programme d'application est une ressource très appropriée pour gérer les fonctionnalités de présentation (ou graphiques) des jeux de résultats, éviter la récupération de données ligne par ligne est primordial pour éviter les problèmes de vitesse d'exécution. L'objectif devrait être de «récupérer» les ensembles de données pertinents au total au moyen des instruments de manipulation de données robustes fournis par le moteur de définition (précis) de la plate-forme SQL afin que vous puissiez optimiser le comportement de votre système.

En outre, l'utilisation d'alias pour renommer une ou plusieurs colonnes dans une certaine portée peut sembler stressante mais, personnellement, je considère cette ressource comme un outil très puissant qui aide à (i) contextualiser et (ii) lever l'ambiguïté du sens et de l' intention attribués au sujet concerné. Colonnes; par conséquent, c'est un aspect qui devrait être soigneusement réfléchi en ce qui concerne la manipulation des données d'intérêt.

Scénarios similaires

Vous pourriez aussi bien trouver utile cette série de messages et ce groupe de messages qui contiennent mon point de vue sur deux autres cas qui incluent des associations de sous-types avec des sous-types mutuellement exclusifs.

J'ai également proposé une solution pour un environnement commercial impliquant un cluster supertype-sous-type où les sous-types ne s'excluent pas mutuellement dans cette (plus récente) réponse .


Notes de fin

1 La définition d'intégration pour la modélisation de l'information ( IDEF1X ) est une technique de modélisation de données hautement recommandable qui a été établie en tant que norme en décembre 1993 par le National Institute of Standards and Technology (NIST)des États-Unis. Il est solidement basé sur (a) certains des travaux théoriques rédigés par le seul auteur du modèle relationnel , c'est-à-dire le Dr EF Codd ; sur (b) la vue entité-relation , développée par le Dr PP Chen ; et également sur (c) la technique de conception de bases de données logiques, créée par Robert G. Brown.

2 Dans IDEF1X, un nom de rôle est une étiquette distinctive attribuée à une propriété (ou attribut) FK afin d'exprimer la signification qu'il détient dans le cadre de son type d'entité respectif.

3 La norme IDEF1X définit la migration de clé comme «le processus de modélisation consistant à placer la clé primaire d'une entité parent ou générique dans son entité enfant ou catégorie comme clé étrangère».

MDCCL
la source
1
Je ne suis pas sûr de comprendre votre demande, mais comme illustré dans la disposition DDL, le Itemtableau comprend une CategoryCodecolonne. Comme mentionné dans la section intitulée «Considérations d'intégrité et de cohérence»:
MDCCL
1
Il est essentiel de souligner que, dans votre environnement commercial, vous devez (1) vous assurer que chaque ligne de «supertype» est à tout moment complétée par son homologue «sous-type» correspondante et, à son tour, (2) garantir que ladite La ligne "sous-type" est compatible avec la valeur contenue dans la colonne "discriminateur" de la ligne "supertype".
MDCCL
1
Il serait très élégant d'appliquer de telles circonstances de manière déclarative mais, malheureusement, aucune des principales plates-formes SQL n'a fourni les mécanismes appropriés pour le faire, à ma connaissance. Par conséquent, le recours au code procédural dans ACID TRANSACTIONS est assez pratique pour que ces conditions soient toujours remplies dans votre base de données. Une autre option consisterait à utiliser TRIGGERS, mais ils ont tendance à rendre les choses désordonnées, pour ainsi dire.
MDCCL
1
Le nœud du problème est qu'aucune implémentation SQL (y compris le dialecte MySQL) ne fournit un support approprié pour les ASSERTIONS, des outils déclaratifs puissants et élégants qui permettraient d'éviter de recourir à des approches procédurales (TRANSACTIONS ou TRIGGERS), ou de travailler de manière redondante comme , par exemple, en répétant inutilement les CategoryColumndans les tableaux qui représentent les sous-types (avec toutes les implications aux niveaux logique [par exemple, anomalies de modification] et physique d'abstraction [par exemple, index supplémentaires, structures plus grandes, etc.]).
MDCCL
2
Jusqu'à ce que les fournisseurs / développeurs de systèmes de gestion de base de données fournissent des ASSERTIONS - l'outil approprié pour cette tâche -, je préfère (a) les approches procédurales - qu'il s'agisse de TRANSACTIONS ou de DÉCLENCHEURS - plutôt que (b) la ligne de conduite redondante, bien que (b) est une possibilité —que je ne recommande pas personnellement—. Bien sûr, le DBA doit gérer soigneusement les autorisations concernant les opérations de manipulation de données valides qui peuvent être exécutées dans la base de données pertinente, ce qui aide décidément beaucoup à maintenir l'intégrité des données.
MDCCL
0

Appelons la table principale Produits. Cela héberge les attributs partagés. Disons alors que nous avons une table Car, une table Plane et une table Boat. Ces trois tables auraient une clé ProductID avec une contrainte FK sur la ligne ID de la table Product. Si vous les voulez tous, rejoignez-les. Si vous voulez juste les voitures, joignez à gauche Cars with Products (ou joignez à droite les produits et les voitures, mais je préfère toujours utiliser les jointures à gauche).

C'est ce qu'on appelle un modèle de données hiearchical. Pour un petit nombre de sous-tableaux, cela peut avoir du sens dans un tableau long (millions de produits).

neManiac
la source
Et puis-je rejoindre les utilisateurs avec des produits?
user5613506
1
En règle générale, vous n'avez pas besoin d'informations utilisateur lorsque vous renvoyez une liste de produits à votre frontal, vous avez besoin des informations produit. Il n'est pas logique de joindre les utilisateurs et les produits et de renvoyer les informations utilisateur identiques pour chaque ligne de produit retournée. Donc, vous filtrez d'abord par type de produit en joignant la table des produits et la sous-table appropriée (voiture, bateau ...), puis vous filtrez par utilisateur en utilisant une clause WHERE. En général, vous voudriez avoir un OwnerID dans la table Products (un FK dans la colonne ID de la table User). Vous devez donc ajouter un WHERE Owner = [Request.User].
neManiac