Come modellare un tipo di entità che può avere diversi set di attributi?


11

Sto riscontrando dei problemi nel ricreare un database con una relazione uno-a-molti (1: M) tra utenti ed elementi .

Questo è piuttosto semplice, sì; tuttavia, ogni articolo appartiene a una determinata categoria (ad esempio un'automobile , una barca o un aereo ) e ogni categoria ha un determinato numero di attributi, ad esempio:

Car struttura:

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

Boat struttura:

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

Plane struttura:

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

A causa di questa diversità nel numero di attributi (colonne), inizialmente ho pensato che sarebbe una buona idea creare una tabella separata per ogni categoria , quindi eviterei diversi NULL e quindi fare un uso migliore dell'indicizzazione.

Sebbene all'inizio sembrasse grandioso, non riuscivo a trovare un modo per creare la relazione tra gli Articoli e le Categorie attraverso il database perché, almeno nella mia modesta esperienza come amministratore di database, durante la creazione di chiavi esterne, informavo esplicitamente un database il nome e la colonna della tabella.

Alla fine, vorrei una solida struttura per archiviare tutti i dati, pur avendo tutti i mezzi per elencare tutti gli attributi di tutti gli elementi che un utente può avere con una query.

Potrei codificare le query dinamiche con il linguaggio lato server , ma ritengo che sia sbagliato e non molto ottimale.

Informazioni aggiuntive

Queste sono le mie risposte ai commenti MDCCL:

1. Quante categorie di articoli di interesse ci sono nel tuo contesto aziendale, tre (ad es. Automobili , barche e aerei ) o più?

In effetti, è molto semplice: ci sono solo cinque categorie in totale.

2. Lo stesso articolo appartiene sempre allo stesso utente (ovvero, una volta che un determinato articolo è stato "assegnato" a un determinato utente non può essere modificato)?

No, potrebbero cambiare. Nello scenario immaginario della domanda, sarebbe come se l' utente A vendesse l'articolo n. 1 per l'utente B , quindi la proprietà deve essere riflessa.

3. Esistono attributi condivisi da alcune o da tutte le categorie ?

Non condiviso ma, dalla memoria, posso dire che sono presenti almeno tre attributi in tutte le categorie .

4. È possibile che la cardinalità della relazione tra utente e oggetto sia da molti a molti (M: N) anziché da uno a molti (1: M)? Ad esempio, nel caso delle seguenti regole aziendali: A User owns zero-one-or-many ItemseAn Item is owned by one-to-many Users

No, poiché gli articoli descrivono un oggetto fisico. Gli utenti ne avranno una copia virtuale, ognuno identificato da un unico GUID v4

5. Per quanto riguarda la seguente risposta a uno dei commenti alla domanda:

"Nello scenario immaginario della domanda, sarebbe come se l' utente A vendesse l'articolo n. 1 per l'utente B , quindi la proprietà deve essere riflessa."

Sembra che tu stia pianificando di tenere traccia dell'evoluzione della proprietà dell'oggetto , per così dire. In questo modo, quali attributi vorresti conservare su tale fenomeno? Solo la modifica dell'attributo che indica l' utente specifico che è il proprietario di un articolo specifico ?

No, non proprio. La proprietà potrebbe cambiare, ma non ho bisogno di tenere traccia del precedente proprietario .

Risposte:


18

Secondo la tua descrizione dell'ambiente di business in esame, esiste una struttura di sottotipo di sottotipo che comprende Item - il supertipo - e ciascuna delle sue categorie , ovvero auto , barca e aereo (insieme ad altre due che non sono state rese note) - i sottotipi—.

Descriverò in dettaglio il metodo che seguirò per gestire un simile scenario.

Regole di business

Per iniziare a delineare lo schema concettuale pertinente , alcune delle più importanti regole commerciali finora determinate (limitando l'analisi alle sole tre categorie divulgate , per rendere le cose il più brevi possibile) possono essere formulate come segue:

  • Un utente possiede zero-uno-o-molti articoli
  • Un articolo è di proprietà di un solo utente in un preciso istante
  • Un articolo può essere di proprietà di uno-a-molti utenti in momenti distinti nel tempo
  • Un articolo è classificato esattamente per una categoria
  • Un articolo è, in ogni momento,
    • o un'auto
    • o una barca
    • o un aereo

Diagramma illustrativo IDEF1X

La Figura 1 mostra un diagramma IDEF1X 1 che ho creato per raggruppare le formulazioni precedenti insieme ad altre regole aziendali che sembrano pertinenti:

Figura 1 - Oggetto e categorie Struttura del sottotipo-sottotipo

Supertype

Da un lato, Item , il supertipo, presenta le proprietà o gli attributi comuni a tutte le categorie , ovvero

  • CategoryCode: specificato come FOREIGN KEY (FK) che fa riferimento a Category.CategoryCode e funge da discriminatore del sottotipo , ovvero indica l'esatta Categoria del sottotipo con cui un determinato Articolo deve essere collegato—,
  • OwnerId —distinto come un FK che punta a User.UserId , ma gli ho assegnato un nome di ruolo 2 per riflettere le sue speciali implicazioni in modo più preciso—,
  • Foo ,
  • Bar ,
  • Baz e
  • CreatedDateTime .

sottotipi

D'altra parte, le proprietà che appartengono a ogni particolare Categoria , ovvero

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

sono visualizzati nella casella del sottotipo corrispondente.

Identifiers

Quindi, Item.ItemId PRIMARY KEY (PK) ha migrato 3 nei sottotipi con nomi di ruoli diversi, ad esempio,

  • CarId ,
  • BoatId e
  • PlaneId .

Associazioni reciprocamente esclusive

Come illustrato, esiste un'associazione o relazione di cardinalità one-to-one (1: 1) tra (a) ciascuna occorrenza di supertipo e (b) la sua istanza di sottotipo complementare.

Il simbolo esclusivo del sottotipo raffigura il fatto che i sottotipi si escludono a vicenda, vale a dire che un'occorrenza concreta dell'oggetto può essere integrata da una sola istanza del sottotipo: un'automobile o un aereo o una barca (mai di due o più).

, Ho utilizzato i nomi di segnaposto classici per autorizzare alcune proprietà del tipo di entità, poiché le loro denominazioni effettive non sono state fornite nella domanda.

Layout a livello logico dell'esposizione

Di conseguenza, al fine di discutere una progettazione logica espositiva, ho derivato le seguenti istruzioni SQL-DDL basate sul diagramma IDEF1X visualizzato e descritto sopra:

-- 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)  
);

Come dimostrato, il tipo di superentity e ciascuno dei tipi di subentity sono rappresentati dalla tabella di base corrispondente .

Le colonne CarId, BoatIde PlaneId, vincolati come PK delle tabelle appropriate, aiuto nel rappresentare il nesso logico-livello uno-a-uno mediante vincoli FK § che scegliere la ItemIdcolonna, che viene vincolata come PK della Itemtabella. Ciò significa che, in una vera "coppia", sia le righe del supertipo che quelle del sottotipo sono identificate dallo stesso valore PK; quindi, è più che opportuno menzionarlo

  • (a) collegare una colonna aggiuntiva per contenere i valori surrogati controllati dal sistema da a (b) le tabelle che rappresentano i sottotipi sono (c) del tutto superflue .

§ Al fine di prevenire problemi ed errori relativi alle definizioni dei vincoli CHIAVE (in particolare ESTERI) —situazione a cui si fa riferimento nei commenti —, è molto importante tenere conto della dipendenza dall'esistenza che si verifica tra le diverse tabelle in questione, come esemplificato in l'ordine di dichiarazione delle tabelle nella struttura DDL espositiva, che ho fornito anche in questo SQL Fiddle .

Ad esempio, aggiungere una colonna aggiuntiva con la proprietà AUTO_INCREMENT a una tabella di un database basato su MySQL.

Considerazioni di integrità e coerenza

È fondamentale sottolineare che, nel proprio ambiente aziendale, è necessario (1) assicurarsi che ogni riga del “sottotipo” sia sempre integrata dalla corrispondente controparte del “sottotipo” e, a sua volta, (2) garantire che detto La riga "sottotipo" è compatibile con il valore contenuto nella colonna "discriminatore" della riga "supertipo".

Sarebbe molto elegante applicare tali circostanze in modo dichiarativo ma, sfortunatamente, nessuna delle principali piattaforme SQL ha fornito i meccanismi adeguati per farlo, per quanto ne so. Pertanto, ricorrere al codice procedurale all'interno di TRANSAZIONI ACIDI è abbastanza conveniente in modo che queste condizioni siano sempre soddisfatte nel database. Un'altra opzione sarebbe impiegare TRIGGERS, ma tendono a rendere le cose disordinate, per così dire.

Dichiarazione di viste utili

Avendo una progettazione logica come quella spiegata sopra, sarebbe molto pratico creare una o più viste, ovvero tabelle derivate che comprendono colonne che appartengono a due o più delle tabelle di base pertinenti . In questo modo, ad esempio, è possibile SELEZIONARE direttamente DA tali viste senza dover scrivere tutti i JOIN ogni volta che è necessario recuperare informazioni "combinate".

Dati di esempio

A questo proposito, supponiamo che le tabelle di base siano "popolate" con i dati di esempio mostrati di seguito:

--

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!');

--

Poi, una vista vantaggiosa è quella che riunisce colonne Item, Care 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;

--

Naturalmente, è possibile seguire un approccio simile in modo da poter SELEZIONARE il "pieno" Boate le Planeinformazioni direttamente DA una singola tabella (una derivata, in questi casi).

Dopo di che -se non vi importa circa la presenza di segni di NULL nel risultato sets- con la seguente definizione di vista, è possibile, ad esempio, “raccogliere” le colonne dalle tabelle Item, Car, Boat, Planee 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;

--

Il codice delle viste qui mostrato è solo illustrativo. Naturalmente, fare alcuni esercizi di prova e modifiche potrebbe aiutare ad accelerare l'esecuzione (fisica) delle query a portata di mano. Inoltre, potrebbe essere necessario rimuovere o aggiungere colonne a tali viste a seconda delle esigenze aziendali.

I dati di esempio e tutte le definizioni della vista sono incorporati in questo SQL Fiddle in modo che possano essere osservati "in azione".

Manipolazione dei dati: codice dei programmi applicativi e alias di colonna

L'uso del codice dei programmi applicativi (se questo è ciò che intendi per "codice specifico lato server") e gli alias di colonna sono altri punti significativi che hai sollevato nei commenti seguenti:

  • Sono riuscito a risolvere il problema [un JOIN] con il codice specifico lato server, ma in realtà non voglio farlo - E - l'aggiunta di alias a tutte le colonne potrebbe essere "stressante".

  • Molto ben spiegato, grazie mille. Tuttavia, come sospettavo, dovrò manipolare il set di risultati quando si elencano tutti i dati a causa delle somiglianze con alcune colonne, dal momento che non voglio usare diversi alias per mantenere la dichiarazione più pulita.

È opportuno indicare che mentre si utilizza il codice del programma applicativo è una risorsa molto adatta per gestire le caratteristiche di presentazione (o grafica) dei set di risultati, evitare il recupero dei dati riga per riga è fondamentale per evitare problemi di velocità di esecuzione. L'obiettivo dovrebbe essere "recuperare" i set di dati pertinenti in toto mediante i solidi strumenti di manipolazione dei dati forniti dal motore (precisamente) impostato della piattaforma SQL in modo da poter ottimizzare il comportamento del sistema.

Inoltre, l'utilizzo di alias per rinominare una o più colonne in un determinato ambito può sembrare stressante ma, personalmente, vedo tale risorsa come uno strumento molto potente che aiuta a (i) contestualizzare e (ii) chiarire il significato e l' intenzione attribuiti al colonne; quindi, questo è un aspetto che dovrebbe essere meditato a fondo rispetto alla manipolazione dei dati di interesse.

Scenari simili

Potresti anche trovare di aiuto questa serie di post e questo gruppo di post che contengono la mia opinione su altri due casi che includono associazioni di sottotipi di sottotipo con sottotipi reciprocamente esclusivi.

Ho anche proposto una soluzione per un ambiente aziendale che coinvolge un cluster di sottotipi di sottotipi in cui i sottotipi non si escludono a vicenda in questa (nuova) risposta .


Note finali

1 Integration Definition for Information Modeling ( IDEF1X ) è una tecnica di modellazione dei dati altamente raccomandabile che è stata stabilita come standard nel dicembre 1993 dal National Institute of Standards and Technology (NIST)degli Stati Uniti. È solidamente basato su (a) alcune delle opere teoriche create dall'unico creatore del modello relazionale , ovvero il Dr. EF Codd ; su (b) il punto di vista entità-relazione , sviluppato dal Dr. PP Chen ; e anche su (c) la tecnica di progettazione del database logico, creata da Robert G. Brown.

2 In IDEF1X, un nome ruolo è un'etichetta distintiva assegnata a una proprietà (o attributo) FK per esprimere il significato che detiene nell'ambito del rispettivo tipo di entità.

3 Lo standard IDEF1X definisce la migrazione delle chiavi come "Il processo di modellizzazione del posizionamento della chiave primaria di un'entità padre o generica nella sua entità figlio o categoria come chiave esterna".


1
Non sono sicuro di aver compreso la tua richiesta, ma come illustrato nel layout DDL, la Itemtabella include una CategoryCodecolonna. Come menzionato nella sezione intitolata "Considerazioni sull'integrità e la coerenza":
MDCCL

1
È fondamentale sottolineare che, nel proprio ambiente aziendale, è necessario (1) assicurarsi che ogni riga del “sottotipo” sia sempre integrata dalla corrispondente controparte del “sottotipo” e, a sua volta, (2) garantire che detto La riga "sottotipo" è compatibile con il valore contenuto nella colonna "discriminatore" della riga "supertipo".
MDCCL

1
Sarebbe molto elegante applicare tali circostanze in modo dichiarativo ma, sfortunatamente, nessuna delle principali piattaforme SQL ha fornito i meccanismi adeguati per farlo, per quanto ne so. Pertanto, ricorrere al codice procedurale all'interno di TRANSAZIONI ACIDI è abbastanza conveniente in modo che queste condizioni siano sempre soddisfatte nel database. Un'altra opzione sarebbe impiegare TRIGGERS, ma tendono a rendere le cose disordinate, per così dire.
MDCCL

1
Il nocciolo della questione è che nessuna implementazione SQL (incluso il dialetto MySQL) fornisce un supporto adeguato per ASSERTIONS, strumenti dichiarativi potenti ed eleganti che aiuterebbero ad evitare il ricorso ad approcci procedurali (TRANSAZIONI o TRIGGER) o che lavorino in modo ridondante come , ad esempio, ripetendo inutilmente il CategoryColumnnelle tabelle che rappresentano i sottotipi (con tutte le implicazioni a livello logico [ad esempio, anomalie di modifica] e fisici dell'astrazione [ad esempio, indici extra, strutture più grandi, ecc.]).
MDCCL

2
Fino a quando uno qualsiasi dei fornitori / sviluppatori del sistema di gestione del database non fornirà ASSERZIONI - lo strumento adeguato per questo compito—, preferisco (a) gli approcci procedurali - sia esso TRANSAZIONI o TRIGGER - rispetto a (b) il corso dell'azione ridondante, sebbene (b) è una possibilità — che personalmente non consiglio—. Naturalmente, il DBA deve gestire attentamente le autorizzazioni relative alle operazioni di manipolazione dei dati valide che possono essere eseguite nel database di pertinenza, il che aiuta decisamente molto a mantenere l'integrità dei dati.
MDCCL

0

Consente di chiamare la tabella principale Prodotti. Questo ospita gli attributi condivisi. Quindi diciamo che abbiamo un tavolo Car, un tavolo Plane e un tavolo Boat. Queste tre tabelle avrebbero una chiave ProductID con un vincolo FK sulla riga ID della tabella Product. Se li vuoi tutti, unisciti a loro. Se vuoi solo le auto, lascia unire Auto con prodotti (o unisci prodotti e automobili, ma preferisco usare sempre i join sul lato sinistro).

Questo è chiamato un modello di dati hiearchical. Per un numero basso di sotto-tabelle potrebbe avere senso in una lunga tabella (milioni di prodotti).


E poi mi unisco agli utenti con i prodotti?
user5613506

1
In genere non sono necessarie informazioni sull'utente quando si restituisce un elenco di prodotti al front-end, sono necessarie le informazioni sul prodotto. Non ha senso unirsi a Utenti e Prodotti e restituire le stesse informazioni utente per ogni riga di prodotto restituita. Quindi prima filtrate per tipo di prodotto unendo la tabella dei prodotti e la sotto-tabella appropriata (Auto, Barca ...) e poi filtrate per Utente usando una clausola WHERE. Generalmente si desidera avere un ID proprietario nella tabella Prodotti (un FK nella colonna ID della tabella Utente). Quindi aggiungere un WHERE Owner = [Request.User].
neManiac,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.