DB design: qual è un buon modo per gestire le proprietà del gioco?

6

Devo mantenere una manciata di proprietà per diversi giochi nel DB relazionale. Nessun altro tipo di DB può essere utilizzato. Il numero di giochi sarà intorno a 4-5, e il numero di proprietà è (per ora) un po 'piccolo, ma forse crescerà più tardi. È quasi garantito che tutti i giochi avranno le stesse proprietà (stesse "chiavi" da dire, non gli stessi valori). Il mio preferito per ora è questo:

CREATE TABLE GamesProperties
(
  propertiesId NUMBER(38,0) NOT NULL,
  gameId VARCHAR2(16 CHAR) NOT NULL,
  cancelPeriod NUMBER(4,0) DEFAULT 5 NOT NULL,
  prizeName VARCHAR2(16 CHAR) NOT NULL,
  CONSTRAINT PK_TGP PRIMARY KEY (propertiesId),
  CONSTRAINT UC_TGP_gid UNIQUE (gameId)
);

Ma un collega ha detto che nel mio progetto dovremmo modificare la tabella ogni volta che è necessario aggiungere una nuova proprietà (che sono d'accordo) e che preferirebbe vedere qualcosa del genere:

CREATE TABLE GamesProperties
(
  propertiesId NUMBER(38,0) NOT NULL,
  gameId VARCHAR2(16 CHAR) NOT NULL,
  propertyName VARCHAR2(16 CHAR) NOT NULL,
  propertyValue VARCHAR2(16 CHAR) NOT NULL,
  CONSTRAINT PK_TGP PRIMARY KEY (propertiesId)
);

Nella sua soluzione non dovremmo modificare la tabella in futuro, ma perdiamo informazioni sul tipo di dati di una proprietà che, a mio parere, è uno svantaggio principale.

Mi piacerebbe sapere quali sono altri pro e contro per ciascuna soluzione, in modo da poter prendere una decisione informata sul design che useremo.

    
posta TheJavaGuy-Ivan Milosavljević 10.06.2016 - 15:18
fonte

5 risposte

7

Il design del tuo collaboratore è un antipattern noto come "valore di attributo di entità". All'inizio sembra davvero attraente a causa della sua flessibilità, ma gli sviluppatori esperti sanno che è una pessima idea perché elimina tutti i vantaggi dell'utilizzo di un database relazionale in primo luogo.

Quando si ha una tabella (di stile normale) che descrive gli attributi degli oggetti dati, il motore di database conosce tutto sull'oggetto. Sa quali sono i tipi di dati di ciascun membro. Conosce (tramite chiavi esterne e altri vincoli) il modo in cui i dati dovrebbero riguardare altri tipi di dati e impedisce che aumenti corrotto. Conosce (tramite indici) quali membri sono fondamentali per la ricerca ed è in grado di ottimizzare le query in base al loro valore e così via.

EAV lancia tutto questo fuori dalla finestra. Non hai tipi di dati distinti; tutto è il tipo della colonna Value (probabilmente VARCHAR), che può avere ramificazioni oltre l'ovvio immediato. (Divertiti con un SUM di valori che dovrebbero essere tutti numeri quando sono tutte stringhe, ad esempio ...) Non hai integrità referenziale, perché non puoi richiedere che i valori di una colonna corrispondano a un'altra colonna quando tutto ciò che hai è una colonna generica di "valore" in cui tutto viene gettato. La generazione di query non banali che richiederebbero join diventa un caos contorto di auto-join che nessuno è in grado di capire, e quanto più grande e complesso diventa il sistema, tanto più da incubo si prova a scrivere delle domande.

Sì, l'utilizzo di tabelle in stile normale richiede un po 'più di lavoro di installazione in anticipo, ma è funzionale a uno scopo: garantisce che tutto sia impostato correttamente in modo che l'effettivo utilizzo del database funzioni senza intoppi. Considerando che dovrai solo impostare le cose una volta, ma poi usarle ripetutamente, cercare di evitare i costi di configurazione andando ad EAV (il che rende l'utilizzo più difficile) è un caso serio di ottimizzazione prematura!

Segui il tuo piano, non quello del tuo collega.

    
risposta data 10.06.2016 - 17:37
fonte
2

But a coworker said that in my design we would have to alter table every time we need to add new property (which I agree)

Anch'io sono d'accordo. Qual è il problema esattamente? Quando aggiungi nuove funzionalità a volte devi modificare il database. Mi sembra abbastanza giusto, so come affrontarlo facilmente.

In his solution we wouldn't have to alter table at all in the future, but we lose information about datatype of a property which I think is a major downside.

Lo svantaggio principale di questo modello (a volte chiamato Entity-Attribute-Value) è il seguente:

  • La query diventa complessa. Ancora peggio quando devi gestire più tipi di dati.
  • Non puoi creare attributi obbligatori
  • Perdere l'integrità referenziale

È noto come anti-pattern (ti consiglio di leggere il libro di SQL Antipatterns, a cui fa riferimento all'interno di esso).

Quindi inutile dire che ti consiglierei di mantenere la soluzione originale. E se è così doloroso per te modificare un tavolo quando aggiungi una nuova funzione, penso che ci sia un altro problema più grande da qualche altra parte.

    
risposta data 10.06.2016 - 17:38
fonte
1

Mi piace che i tuoi colleghi lavorino meglio con un leggero aggiustamento.

A seconda del contenuto sia di propertyName che di propertyValue , un'ulteriore decomposizione potrebbe essere migliore per non avere dati ridondanti.

Avresti quindi due tabelle:

  • PropertyName (con Id e Name campi)
  • GameProperty (l'attuale propertyName sarebbe idPropertyName invece, una chiave esterna a una chiave primaria della tabella PropertyName )

o se è probabile che i valori di propertyValue vengano ripetuti, avere 3 tabelle anziché due:

  • PropertyName (con Id e Name campi)
  • PropertyValue (con Id , Value e idPropertyName - essendo un FK alla tabella PropertyName )
  • GameProperty (gli attuali attributi propertyName e propertyValue verrebbero sostituiti con idPropertyValue - essendo un FK alla tabella PropertyValue )

Oltre alla decomposizione, non mi piace il nome della tua chiave primaria, è ambigua. Un'idea migliore potrebbe essere denominarla solo Id o denominarla dalla tabella di cui è la chiave primaria, ad esempio GamePropertiesId per la tabella GameProperties .

    
risposta data 10.06.2016 - 16:23
fonte
0

Che ne dici di questo:

+-----------+       +------------------+       +-------------------+
|           |       |                  |       |                   |
|           |       |                  |       |                   |
|   Game    |       |    GameProperty  |       |   Property        |
|           +------->                  <-------+   ID (PK)         |
|   ID (PK) |       | *  GameID  (FK)  +       |   Name            |
|   Name    |       | *  PropertyID (FK)       |   DataType (FK)   |
|   ...     |       |    ValueID       |       |                   |
|           |       |                  |       |                   |
|           |       |                  |       |                   |
|           |       |*PK               |       |                   |
+-----------+       +-+-----+------+---+       +------^------------+
                      |     |      |                  |
                      |     |      |                  |
 +--------------------v--+  |      |                  |
 |                       |  |      |           +------+------------+
 |  PropertyNumberValue  |  |      |           |                   |
 |  ID                   |  |      |           |   RefDataType     |
 |  Value (Number(x,y))  |  |      |           |                   |
 |                       |  |      |           |   ID              |
 |                       |  |      |           |   Name            |
 |                       |  |      |           |   ...             |
 |                       |  |      |           |                   |
 |                       |  |      |           |                   |
 +-----------------------+  |      |           |                   |
                            |      |           +-------------------+
                            |      |
                            |      |
  +-----------------------+ |  +---v------------------------+
  |                       <-+  |                            |
  |  PropertyStringValue  |    |    PropertyDateTimeValue   |
  |  ID                   |    |    ID                      |
  |  Value Varchar(x)     |    |    Value DateTime          |
  |                       |    |                            |
  |                       |    |                            |
  +-----------------------+    +----------------------------+

Nota : GameProperty.ValueID fa riferimento a una delle 3 tabelle basate su Proerty.DataType . Ciò consente di mantenere il tipo di dati dei dati fuori dai campi del DB varchar, sebbene complichi leggermente il recupero del valore.

    
risposta data 10.06.2016 - 17:12
fonte
0

Il design EAV è un modo per consentire agli utenti / amministratori un modo per creare campi personalizzati senza richiedere ulteriore codifica per ciascun elemento. Possono essere letti e applicati dinamicamente. Sono più adatti per i valori di tipo di ricerca. Non c'è nulla nella descrizione della tua app che ha bisogno di questa funzionalità, quindi il tuo collaboratore sta usando un cattivo design.

Se la creazione di campi in un database è così difficile da codificare, dovresti usare qualcosa di simile a ORM per occuparti di questo per te.

    
risposta data 11.06.2016 - 16:41
fonte

Leggi altre domande sui tag