Possibili vantaggi della memorizzazione di più valori in un campo di una riga anziché come righe separate


11

Durante la nostra ultima riunione settimanale, una persona che non ha esperienza in amministrazione dei database ha sollevato questa domanda:

"Ci sarebbe uno scenario che giustifica l'archiviazione dei dati in linea (stringa) anziché diverse righe?"

Supponiamo che un tavolo chiamato countryStatesdove vogliamo memorizzare gli stati di un paese; Userò gli Stati Uniti per questo esempio e non elencherò tutti gli Stati per motivi di pigrizia.

Lì avremmo due colonne; uno ha chiamato Countrye l'altro ha chiamato States. Come discusso qui , e proposto dalla risposta di @srutzky , PKsarà il codice definito da ISO 3166-1 alpha-3 .

La nostra tabella sarebbe simile a questa:

+---------+-----------------------+-------------------------------------------------------+
| Country | States                | StateName                                             |
+---------+-----------------------+-------------------------------------------------------+
| USA     | AL, CA, FL,OH, NY, WY | Alabama, California, Florida, Ohio, New York, Wyoming |
+---------+-----------------------+-------------------------------------------------------+

Quando ha posto la stessa domanda a un amico sviluppatore, ha detto che dal punto di vista delle dimensioni del traffico dati, questo potrebbe essere utile, ma non se dobbiamo manipolare questi dati. In questo caso dovrebbe esserci un'intelligence sul codice dell'applicazione che potrebbe trasformare questa stringa in un elenco (diciamo che il software che ha accesso a questa tabella deve creare una casella combinata).

Abbiamo concluso che questo modello non è molto utile, ma ho avuto il sospetto che potrebbe esserci un modo per renderlo utile.

Quello che vorrei chiedere è se qualcuno di voi ha già visto, sentito o fatto qualcosa del genere in un modo che funziona davvero .


Ora immagina di avere una seconda tabella, "vendite", che contiene i dati per ogni vendita avvenuta insieme al codice di stato in cui è avvenuta la vendita. Come scriveresti una query che genera un rapporto con colonne (StateName, TotalSalesAmount)? Difficile, vero?
zgguy,

Esattamente. Inoltre, non sono d'accordo con questo modello. Rimaniamo bloccati in qualsiasi momento in cui abbiamo bisogno di recuperare qualsiasi tipo di dati (o dati utili se vuoi).
Human_AfterTutto il

Un possibile scenario potrebbe essere la memorizzazione di variabili. Conservare a;b;c, utilizzare il front-end per analizzare la stringa si quindi ottenere a, b, ce continuare l'esecuzione di fare qualcosa con loro, forse ?. Senti che potrebbe soddisfare una sorta di bisogno specifico in quel modo ... A pensarci bene, no. Puoi sempre archiviare gli ID, unirti ai tuoi tavoli e creare una stringa concatenata che può inviare contenuto alla FE ...
Nelz

Per essere onesti (almeno per me ;-), ho proposto di utilizzare i codici Paese a 2 caratteri :-) in quell'altra risposta .
Solomon Rutzky,

2
Si noti che nessuno ha scrupoli nel memorizzare il valore "Alabama" in una colonna anziché avere una tabella separata con colonne STATE, N & C per "state il nome STATE ha l'ennesimo carattere C". Perché o 1. non intendiamo interrogare sui caratteri dei nomi o 2. non ci dispiace chiamare una funzione NTH_CHAR (N, S) che restituisca "l'ennesimo carattere della stringa S" su ogni riga con un nome se lo facciamo . (Vs JOIN e altri operatori relazionali che eliminano alcune di queste righe tramite la tabella aggiuntiva.) Idem per numeri interi e NTH_DIGIT (N, I). È sempre un giudizio su ciò che in un particolare database è relazionalmente atomico.
Philipxy,

Risposte:


13

Per cominciare, l'attuale titolo della domanda che si riferisce alla "memorizzazione di dati come stringa anziché come colonne" è un po 'confuso. Quando si parla di archiviare i dati come stringhe anziché qualcos'altro, ciò di solito si riferisce alla serializzazione di tutto in un formato stringa anziché in un tipo di dati appropriato / forte (ad es. INTO DATETIME). Ma se si chiede di archiviare i dati come valori multipli in un singolo campo rispetto a righe separate, è un po 'diverso. E per essere onesti, mentre la concatenazione dei valori viene eseguita più facilmente con le stringhe, può anche essere eseguita con INTe BINARYtipi, sia mediante mascheratura di bit sia riservando in modo simile determinate posizioni per avere significati diversi. Dal momento che la seconda interpretazione è ciò che viene effettivamente chiesto, basato sul testo della domanda, affrontiamolo.

In una parola: No. Se stai memorizzando punti di dati effettivi, causerà solo dolore (in termini di codice e prestazioni) in quanto non è una complicazione inutile. Se si tratta di un valore che verrà sempre e solo archiviato come singola unità, aggiornato come singola unità e mai smontato all'interno del database, ciò potrebbe andare bene poiché è quasi analogo alla memorizzazione di un'immagine o PDF. Altrimenti, qualsiasi tentativo di analizzare i dati invaliderà utilizzando qualsiasi indice (ad es. Utilizzando LIKE '%something%', o CHARINDEX, o PATINDEX, o SUBSTRING, ecc.).

Se hai bisogno di memorizzare valori separati in un singolo campo di una singola riga, allora ci sono mezzi più appropriati per farlo: XML o JSON. Questi sono formati analizzabili ( XML / JSON ) e XML può anche essere indicizzato . Ma idealmente questi dati verrebbero archiviati in campi correttamente digitati in modo che possano essere veramente utili.

E, per favore, non dimenticare che lo scopo di un RDBMS è di archiviare i dati in modo tale che possano essere recuperati e manipolati nel modo più efficiente possibile, entro i vincoli imposti dalla conformità ACID . Il recupero di valori concatenati è abbastanza grave a causa della necessità di analizzare prima i valori e questo non è indicizzabile. Ma manipolare spesso significa sostituire l'intero BLOB solo per aggiornarne una parte (supponendo che non esista un modello da usare con una REPLACEfunzione). Il tipo di dati XML consente almeno il DML XML per aggiornamenti semplicistici, sebbene questi non siano ancora veloci come un semplice aggiornamento di dati correttamente modellati.

Inoltre, dato uno scenario come quello mostrato nella domanda precedente, concatenando tutti gli StateCodes insieme, non si sarebbe in grado di chiave esterna (in entrambe le direzioni) quei valori.

E se i requisiti aziendali cambiassero nel tempo e fosse necessario tenere traccia delle proprietà aggiuntive di questi articoli? In termini di "stati", che dire delle capitali, della popolazione, di un ordinamento o di qualcos'altro? Memorizzato correttamente come righe è possibile aggiungere più colonne per proprietà aggiuntive. Certo, puoi avere più livelli di dati analizzabili, come |StateCode,Capital,Population |StateCode,Capital,Populate|...ma spero che chiunque possa vedere il problema crescere esponenzialmente senza controllo. Naturalmente, questo particolare problema è piuttosto facilmente risolto con i formati XML e JSON, e questo è il loro valore come menzionato sopra. Ma si sarebbe ancora bisogno di una molto buona ragione per utilizzare uno di questi come un mezzo iniziali di modellazione in quanto né potrà mai essere il più efficiente utilizzando i campi discreti in file separati.


9

In realtà ho usato qualcosa del genere per uno scopo molto limitato. Abbiamo creato una tabella di intestazioni per i file di output. Sono stati appositamente costruiti e per lo più erano solo le intestazioni di colonna ma non del tutto. Quindi i dati sembravano qualcosa del genere

OutputType   OutputHeader
PersonalData Name|Address|City|State|Zip
JobInfo      Name|JobName|JobTitle

In sostanza sembrava che fosse un elenco delimitato. E in un certo senso lo era. Ma per i nostri scopi era un'unica lunga stringa.

Questo è il trucco qui. Se non hai mai intenzione di analizzare l'elenco, vale la pena salvarlo. Se tuttavia è necessario o addirittura necessario analizzare l'elenco, vale la pena dedicare spazio e tempo extra per suddividerlo e salvarlo in righe separate.


1

L'ho usato una volta con un tavolo piuttosto piccolo, ad esempio:

CREATE TABLE t1 (
  ID number,
  some_feature   varchar2(100),
  valid_channels  varchar2(100));

CREATE TABLE channel_def (
  channel varchar2(100));

E quindi archiviare i valori CRM,SMS,SELF-CAREin valid_channel.

L'intera tabella ha qualcosa come 10 record. valid_channelcontiene valori che dovrebbero effettivamente essere in una tabella di collegamento che descrive la relazione molti-a-molti. Il tavolo t1non verrà utilizzato intensamente, quindi abbiamo appena deciso di percorrere questa strada. Alcuni politici sono stati coinvolti in questa decisione, però (vedi sotto).

Ma in generale lo evito, non è 3NF.

Il posto in cui lavoro attualmente ha dozzine di tali colonne dappertutto. La loro giustificazione è che semplifica le loro query: invece di unire tre tabelle usando la tabella di collegamento possono usare direttamente la tabella di definizione LIKE. Per esempio

SELECT * 
  FROM t1 
 INNER JOIN channel_def cd
    ON ','||t1.valid_channels||',' LIKE '%,'||cd.channel||',%';

Horrible + su Oracle disabilita l'uso dell'indice a causa dell'avvio '%,'.


Quale sarebbe più lento: LIKEo un semplice join?
Human_AfterTutto il

È meglio avere un join su una colonna indicizzata o almeno con un vincolo referenziale (FK) su di essa. Inoltre, i join vengono in genere eseguiti su un PK dell'altra tabella, che viene indicizzato per impostazione predefinita (almeno su Oracle). Se stai chiedendo informazioni sul caso particolare in questione (vedi sopra), il piano di esecuzione molto probabilmente direbbe che era lo stesso, dato che era un piccolo tavolo.
Robotron,

@Human_AfterTutto LIKEsarebbe più lento, specialmente se i dati sono correttamente modellati per utilizzare un TINYINTcampo PK in channel_def. Quindi deve solo confrontare un singolo byte tra le due tabelle. Qui deve analizzare la stringa, carattere per carattere (almeno fino a quando la condizione non è soddisfatta), e sta effettuando una ricerca senza distinzione tra maiuscole e minuscole (in base alla definizione della tabella data che non mostra un _BIN2confronto utilizzato). Ciò invalida anche gli indici su SQL Server. Ho risolto questo problema nella mia risposta dicendo che l'analisi non può utilizzare gli indici. Ho appena aggiornato la mia risposta per renderlo più chiaro.
Solomon Rutzky,

1
@Human_AfterAll Direi che questa decisione di modellistica è nata da una mancanza di esperienza e conoscenza (e talvolta pigrizia). Un ulteriore JOIN è tutto ciò che viene salvato, ma ciò che viene sacrificato è la capacità di Chiave esterna che impedirebbe l'accesso ai dati completamente falsi (anche se non corrisponderebbe alla LIKEclausola e produrrebbe risultati strani, può comunque causare altri problemi o almeno rendere il debug più difficile / più lungo). Inoltre rende valid_channelspiù complicato l'aggiornamento del campo. Questo non vuol dire che non funziona, non c'è proprio una buona ragione per farlo.
Solomon Rutzky,

"mancanza di esperienza" - la cosa peggiore è che questa particolare decisione progettuale è stata imposta da un membro dello staff senior ...
Robotron

1

Questo è stato fatto qui su SE. Come scrive Marc Gravell :

... Dopo qualche riflessione e considerazione, abbiamo optato per una rappresentazione naturale delimitata da pipe (bar), con pipe iniziali / finali, quindi “.net c #” diventa semplicemente “| .net | c # |”. Questo ha delle virtù:

  • molto semplice da analizzare
  • l'aggiornamento in blocco e la rimozione dei tag possono essere effettuati con una semplice sostituzione (inclusi i tubi, per evitare la sostituzione di corrispondenze a metà tag)
  • ...

Questo "nuovo formato" è stato il passo successivo rispetto al "vecchio formato" che era un po 'diverso ed è stato scelto per utilizzare la funzionalità di ricerca full-text di SQL Server, quindi alcuni dei vantaggi non sono rilevanti se lo si fa da zero.

Presumibilmente non hanno completamente normalizzato la cosa sia per la quantità di lavoro che per le prestazioni.


0

Bene, un possibile vantaggio principale dell'utilizzo delle stringhe e di altri tipi di dati è l'invio da SQL Server a C #, C, C ++ (ecc.) Utilizzando SQLCLR quando potrebbero essere necessarie prestazioni eccezionali. Potresti persino creare una vista o una procedura memorizzata per rappresentare i dati relazionali in modo non relazionale, come hai fatto nell'esempio sopra per questo scopo.

Vedi questo esempio:

http://aboutsqlserver.com/2013/07/22/clr-vs-t-sql-performance-considerations/

per Wikipedia: SQL CLR o SQLCLR (SQL Common Language Runtime) è la tecnologia per l'hosting del motore di runtime di linguaggio comune Microsoft .NET all'interno di SQL Server. SQLCLR consente al codice gestito di essere ospitato ed eseguito nell'ambiente Microsoft SQL Server.


2
Ciao. Potete per favore fornire maggiori dettagli qui. Non sono sicuro di come questo sia un vantaggio della memorizzazione dei dati in modi non tradizionali. Semmai, è un vantaggio di SQLCLR essere in grado di gestire meglio formati di dati alternativi se questi devono esistere. Ma questo non è un motivo per preferire un formato di dati alternativo. In quanto tale, non credo proprio che questo risponda alla domanda.
Solomon Rutzky,

Il link all'articolo spiega i vantaggi con pro e contro. Inoltre, ho menzionato l'archiviazione dei dati in modo relazionale e ai fini del CLR convertendoli in non relazionali con una vista o procedura memorizzata. La tua domanda era "Ci sarebbe uno scenario che giustifica l'archiviazione dei dati in linea (stringa) anziché più righe?" E la mia risposta è stata sì, anche se preferisco una vista o una procedura memorizzata allo scopo di interagire con il CLR.
Sting

0

A mio avviso, la risposta sarebbe no. Non ho usato questo approccio e lo eviterei: non riesco a pensare a un motivo per cui seguirei questa strada. Ti stai inclinando verso il mondo di JSON / NoSQL con un array.

Avevamo scelte di progettazione simili in un ruolo precedente in base al quale il team di architetti voleva avere un campo "Dati" che era stato delimitato e quindi convertito in binario. Alla fine non abbiamo seguito questa strada per alcuni motivi.

Se dovessi unirti a questo tipo di dati, sarebbe una brutta esperienza. Anche l'aggiornamento di singoli elementi della stringa sarebbe sgradevole.

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.