È una cattiva pratica consentire i campi definiti dall'utente?


17

In generale, è considerata una cattiva pratica consentire i campi creati dall'utente in un database per una webapp?

Ad esempio, sto realizzando una webapp per l'inventario di casa per mia moglie e vorrà definire i propri campi per diversi articoli. Avevo intenzione di consentirle di creare categorie di articoli e aggiungere "funzionalità" a tali categorie. Le funzioni sarebbero semplicemente chiave / valore memorizzato come stringhe. In questo modo, se avesse una categoria chiamata "CD audio", ad esempio, potrebbe aggiungere funzionalità per cose come "artista", "tracce", ecc. Ma in un'altra categoria come "mobili", potrebbe aggiungere funzionalità per cose come "materiale" "(legno, plastica, ecc.). Quindi qualsiasi elemento potrebbe appartenere a una (o più) categorie, aggiungendo tali funzionalità all'elemento.

Riesco a vedere problemi in cui la ricerca in base a queste funzionalità richiede confronti di stringhe, non esiste alcuna convalida dei dati, ecc. Seguendo una metodologia agile, forse sarebbe meglio che le venissero inventate nuove categorie e attributi e avrei dovuto solo creare nuove tabelle come andiamo. Nel mio esempio, è una piccola base di utenti (2 di noi) e la quantità di record creati sarebbe piccola, quindi non male.

In generale, però, come fanno le persone a gestire qualcosa del genere nella "vita reale"?


4
Hai mai pensato di utilizzare un database orientato ai documenti come MongoDB? È possibile memorizzare un documento per tipo che funge da schema che può anche essere modificato (probabilmente manualmente, data la scala ridotta del progetto).
Andy Hunt,

@AndyBursh uno dei bit "divertenti" con gli attuali postgres è il tipo di dati "json" ( link ). Un tale approccio consentirebbe di archiviare i campi specificati dall'utente in quei dati, er, document, er, qualunque cosa e quindi utilizzare il resto dei campi per cose su cui indicizzare correttamente e simili. Sebbene tutto ciò dipenda dall'uso ed è difficile dire se questo funzionerebbe bene per una particolare applicazione o meno. Ma è qualcosa di cui essere consapevoli.

tutto: grande discussione, grazie per tutte le intuizioni! @AndyBursh Ho sentito parlare di MongoDB ma non l'ho mai veramente letto. Sembra un altro progetto di casa con cui sperimentare ...
zako42

Risposte:


19

Quando inizi ad arrivare a "campi definiti dall'utente", come spesso si trova nei bug tracker, nella gestione delle risorse dei clienti e in strumenti aziendali simili, è che non sono supportati da una tabella con campi bajillion (se lo sono, è probabile che sia un problema di propria).

Invece quello che trovi sono i disegni della tabella dei valori degli attributi delle entità e lo strumento di amministrazione associato per gestire gli attributi validi.

Considera la seguente tabella:

  + -------------- +
  | cosa |
  | -------------- |
  | id |
  | digitare |
  | desc |
  | attr1 |
  | attr2 |
  | attr3 |
  | attr4 |
  | attr5 |
  + -------------- +

Questo è dopo aver aggiunto alcuni attributi. Invece di attr1far finta che legge artisto trackso genreo qualunque attributi la cosa ha. E invece di 5, se fosse 50. Chiaramente questo è ingestibile. Richiede anche un aggiornamento del modello e la ridistribuzione dell'applicazione per gestire un nuovo campo. Non ideale

Consideriamo ora la seguente struttura della tabella:

  + -------------- + + --------------- + + ------------- +
  | cosa | | thing_attr | | attr |
  | -------------- | | --------------- | | ------------- |
  | id | <--- + | thing_id (fk) | +> | id |
  | digitare | | attr_id (fk) | + - + | nome |
  | desc | | valore | | |
  + -------------- + + --------------- + + ------------- +

Hai le tue cose con i suoi campi di base. Hai altri due tavoli. Uno con gli attributi. Ogni campo è una riga nella attrtabella. E poi c'è il thing_attrcon una coppia di chiavi esterne relative al thingtavolo e al attrtavolo. E questo ha quindi un campo valore in cui archiviare qualunque sia il valore del campo per quell'entità.

E ora hai una struttura in cui la tabella attr può essere aggiornata in fase di esecuzione e nuovi campi possono essere aggiunti (o rimossi) al volo senza un impatto significativo sull'applicazione complessiva.

Le query sono un po 'più complesse e anche la convalida diventa più complessa (procedure memorizzate funky o tutto il lato client). È un compromesso nel design.

Considera anche la situazione in cui un giorno devi eseguire una migrazione e torni all'applicazione per scoprire che ora ci sono una mezza dozzina di attributi in più rispetto allo schema che hai distribuito originariamente. Ciò rende possibili brutte migrazioni e aggiornamenti in cui la tabella Valore attributo entità, se utilizzata correttamente, può essere più pulita. (Non sempre, ma può essere.)


Ci sono degli svantaggi nella modifica dello schema in fase di esecuzione? Se l'utente pensa che una cosa abbia bisogno di un nuovo attributo, basta aggiungere dinamicamente una colonna alla tabella?

Se stai lavorando con il sapore appropriato del database nosql, potresti probabilmente farlo (nota che il sapore appropriato del nosql per questo sarebbe probabilmente un archivio di valori-chiave che è, beh, la tabella EAV per quelli relazionali sopra descritti) senza troppi problemi. Tuttavia viene fornito con tutti i compromessi per nosql che sono descritti altrove in grande dettaglio.

Se invece lavori su un database relazionale, devi avere lo schema. L'aggiunta dinamica della colonna significa che alcuni sottoinsieme delle seguenti cose sono vere:

  • Stai eseguendo la programmazione di meta-database. Invece di essere in grado di mappare in modo pulito questa colonna su quel campo con un bel ORM, probabilmente stai facendo cose come select *e poi facendo un codice complesso per scoprire quali sono effettivamente i dati (vedi ResultSetMetaData di Java ) e quindi archiviarli in una mappa ( o qualche altro tipo di dati - ma non dei bei campi nel codice). Questo quindi getta via un bel po 'di tipo e di errore di battitura che hai con l'approccio tradizionale.
  • Probabilmente hai abbandonato l'ORM. Questo significa che stai scrivendo sql grezzo per tutto il codice invece di lasciare che il sistema faccia il lavoro per te.
  • Hai rinunciato a fare aggiornamenti puliti. Cosa succede quando il cliente aggiunge un campo con un nome utilizzato anche dalla versione successiva? Nel sito di matchmaking l'upgrade che vuole aggiungere un hasdatecampo per la memorizzazione di un timestamp è già stato definito come hasdatecon un valore booleano per una partita di successo ... e il tuo upgrade si interrompe.
  • Stai fidando che il cliente non rompe il sistema usando una parola riservata che rompe anche le tue domande ... da qualche parte.
  • Ti sei limitato a un marchio di database. Il DDL di database diversi è diverso. I tipi di database ne sono l'esempio più semplice. varchar2vs texte simili. Il tuo codice per aggiungere la colonna funzionerebbe su MySQL ma non su Postgres o Oracle o SQL Server.
  • Ti fidi che il cliente abbia effettivamente aggiunto bene i dati ? Certo, l'EAV è tutt'altro che ideale ma ora hai alcuni nomi di tabella oscuri orrendi che lo sviluppatore non ha aggiunto, con il tipo di indice errato (se presente), senza vincoli aggiunti nel codice dove è necessario essere e così via.
  • Hai assegnato i privilegi di modifica dello schema all'utente che esegue l'applicazione. Little Bobby Drop Tables non è possibile quando si è limitati a SQL piuttosto che a DDL (sicuramente si può fare un delete * from studentsinvece, ma non si può davvero rovinare il database in modi sbagliati). Il numero di cose che possono andare storte nell'accesso allo schema a seguito di un incidente o di attività dannose sale alle stelle.

Questo si riduce davvero a "non farlo". Se lo vuoi davvero, scegli uno schema noto della struttura della tabella EAV o un database interamente dedicato a questa struttura. Non permettere alle persone di creare campi arbitrari in una tabella. Il mal di testa non ne vale la pena.


4
Hai anche reinventato il database.
user253751

1
@immibis ha aggiunto un livello in cui l'utente può amministrare senza alterare il resto del database o richiedere una ridistribuzione per aggiornare il modello.

1
@immibis EAV ha discusso a lungo nei circoli di database relazionali per anni. In teoria, non è necessario, ma in pratica non puoi fare certe cose senza di essa.
Ross Patterson,

1
@ShivanDragon che va all'approccio NoSQL. L'archivio documenti memorizza solo documenti e non impone uno schema. Come tale, l'aggiunta e la rimozione di campi e l'analisi dei documenti è completamente al di fuori dell'ambito del database stesso (e hai scritto il tuo modello per adattarlo). È un insieme completamente diverso di compromessi rispetto ai compromessi del database relazionale per una struttura EAV.


5

Fare questo bene è difficile.

Per un'applicazione una tantum come quella che stai pianificando, puoi ovviamente aggiungere una colonna per ogni campo e fornire un'interfaccia utente che renda la definizione del campo da parte di utenti non addestrati più sicura rispetto a fornire loro una riga di comando SQL. Oppure potresti seguire il temuto modello Entità-Attributo-Valore , che è una risposta classica, anche se un po 'spaventosa, a questo tipo di problema. La creazione dell'interfaccia utente per la definizione dei campi EAV è in genere molto più complessa rispetto alle colonne del database e le query possono diventare piuttosto pelose, ma per un gran numero di campi ( ovvero schemi a matrice molto sparsa), potrebbe essere l'unico modo per ottenere il lavoro fatto.


In sintesi: piccolo progetto == KISS. Agile fino a terra.
Encaitar,

Il problema con gli aggiornamenti della tabella del database è che, a seconda della quantità di dati e degli indici richiesti (i campi personalizzati richiedono spesso funzionalità di ricerca), la query di modifica della tabella può richiedere un tempo enorme. Per farla breve, MySQL e altri database relazionali semplicemente non sono un buon supporto per questo tipo di requisiti.
Oddman,

0

Ho incrociato qualcosa di simile recentemente.

Ho fatto 2 tavoli.

1: table Objects 
    Id , name, type

Lui è tutti i tuoi oggetti. Ne hai impostato il nome.

E un tipo di questo oggetto: - per me i tipi disponibili erano inventario, inventario_item, ufficio.

E la solita installazione era di n elementi sono figlio o inventario che è anche figlio dell'ufficio e ho usato una tabella di join per unire gli oggetti tra loro

2 table settings 
     organization_Id , title, value , type

La tabella delle impostazioni contiene tutti i nomi dei campi per quel tipo di oggetto specifico e valore in valore.

Proprietà di esempio dell'ufficio

Posizione, telefono, orario di lavoro

E per gli articoli

  • Quantità
  • Prezzo
  • Codice a barre

Ecc., Tutte queste proprietà vengono applicate dal tuo modello e salvate nella tabella delle impostazioni come righe separate (ma usa sostituire non inserire per evitare più righe per lo stesso campo)

Quindi, quando mai desidero un ufficio, lo carico facilmente con tutte le sue relazioni e impostazioni in cui si trovano le impostazioni object_ (oggetti richiesti)

Dopodiché, faccio ruotare tutte le righe dalle impostazioni e il gioco è fatto.

E nel caso volessi che un'impostazione fosse specifica per un articolo in un inventario (non globale) ho impostato object_I'd = sarebbe dalla tabella relazioni object_objects e ho impostato settings.type = relationship_setting

Spero che tu capisca cosa intendo, cercherò di riformattare la risposta quando arrivo a un laptop


2
Suggerimento professionale: non pubblicare post su questo forum dal tuo telefono. L'auto-correzione rende illeggibili parti del tuo post.
BobDalgleish,

Haha bella osservazione :)
Zalaboza,

0

È una cattiva pratica consentire i campi definiti dall'utente?

No, non è una cattiva pratica. È abbastanza comune. In termini di OO questo si chiama eredità. Hai un inventario della classe base e due classi ereditate AudioCD e mobili.

In generale, però, come fanno le persone a gestire qualcosa del genere nella "vita reale"?

Devi decidere come archiviare Item, AudioCD e mobili nel database.

Se easy-query è la cosa più importante per te e db-space / normalization non importa, implementeresti lo schema "table-per-hierarchy".

Se lo spazio / la normalizzazione è più importante per te e le query più complicate non sono un problema per te, implementeresti lo schema "tabella per tipo".

Per ulteriori dettagli, consultare dotnet table-per-type- for -table-per-hierarchy-heritage eredity o java hibernate eredityance .


Non so se questo affronta la domanda. L'utente non sta modificando il codice per creare nuove classi
Colin D
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.