Memorizzazione efficiente di gruppi di coppie chiave-valore con chiavi selvaggiamente diverse


9

Ho ereditato un'applicazione che associa molti diversi tipi di attività a un sito. Esistono circa 100 diversi tipi di attività e ognuno ha un set diverso di 3-10 campi. Tuttavia, tutte le attività hanno almeno un campo data (potrebbe essere qualsiasi combinazione di data, data di inizio, data di fine, data di inizio programmata, ecc.) E un campo persona responsabile. Tutti gli altri campi variano ampiamente e un campo della data di inizio non sarà necessariamente chiamato "Data di inizio".

La creazione di una tabella di sottotipi per ciascun tipo di attività comporterebbe uno schema con 100 diverse tabelle di sottotipi, che sarebbe troppo ingombrante da gestire. La soluzione attuale a questo problema è archiviare i valori dell'attività come coppie chiave-valore. Questo è uno schema molto semplificato del sistema attuale per ottenere il punto.

inserisci qui la descrizione dell'immagine

Ogni attività ha più ActivityField; ogni sito ha più attività e la tabella SiteActivityData memorizza i KVP per ogni SiteActivity.

Ciò rende l'applicazione (basata sul web) molto facile da codificare perché tutto ciò che è veramente necessario fare è passare in rassegna i record in SiteActivityData per una determinata attività e aggiungere un'etichetta e controllo di input per ogni riga a un modulo. Ma ci sono molti problemi:

  • L'integrità è cattiva; è possibile inserire un campo in SiteActivityData che non appartiene al tipo di attività e DataValue è un campo varchar, quindi numeri e date devono essere costantemente lanciati.
  • Il reporting e l'interrogazione ad hoc di questi dati è difficile, soggetto a errori e lento. Ad esempio, ottenere un elenco di tutte le attività di un determinato tipo che hanno una Data di fine in un intervallo specificato richiede pivot e trasmissione di varchars alle date. Gli autori di report odiano questo schema e non li biasimo.

Quindi quello che sto cercando è un modo per archiviare un gran numero di attività che non hanno quasi campi in comune in modo da rendere più semplice la segnalazione. Quello che ho escogitato finora è usare XML per archiviare i dati di attività in un formato pseudo-noSQL:

inserisci qui la descrizione dell'immagine

La tabella Attività conterrebbe l'XSD per ogni attività, eliminando la necessità della tabella ActivityField. SiteActivity conterrebbe l'XML del valore-chiave in modo che ogni attività per un sito sia ora in una singola riga.

Un'attività dovrebbe assomigliare a questa (ma non l'ho ancora completamente chiarita):

<SomeActivityType>
  <SomeDateField type="StartDate">2000-01-01</SomeDateField>
  <AnotherDateField type="EndDate">2011-01-01</AnotherDateField>
  <EmployeeId type="ResponsiblePerson">1234</EmployeeId>
  <SomeTextField>blah blah</SomeTextField>
  ...

vantaggi:

  • L'XSD avrebbe convalidato l'XML, rilevando errori come mettere una stringa in un campo numerico a livello di database, cosa impossibile con il vecchio schema che memorizzava tutto in varchar.
  • Il recordset di KVP utilizzato per creare i moduli Web può essere facilmente riprodotto utilizzando select ... from ActivityXML.nodes('/SomeActivityType/*') as T(r)
  • Una sottoquery xpath dell'XML potrebbe essere utilizzata per produrre un set di risultati con colonne per la data di inizio, la data di fine, ecc. Senza usare un perno, qualcosa come select ActivityXML.value('.[@type=StartDate]', 'datetime') as StartDate, ActivityXML.value('.[@type=EndDate]', 'datetime') as EndDate from SiteActivity where...

Ti sembra una buona idea? Non riesco a pensare ad altri modi per memorizzare un numero così elevato di insiemi di proprietà differenti. Un altro pensiero che avevo era mantenere lo schema esistente e tradurlo in qualcosa di più facilmente interrogabile in un data warehouse, ma non avevo mai progettato uno schema a stella prima e non avrei idea da dove cominciare.

Domanda aggiuntiva: se definisco un tag con un tipo di dati di data nell'XSD xs:date, SQL Server lo indicizzerà come valore di data? Sono preoccupato se eseguo una query per data, sarà necessario eseguire il cast della stringa della data su un valore di data e aumentare le possibilità di utilizzare un indice.


Quanto devono essere aggiornati i dati per i report? I rapporti raggiungeranno la produzione?
James Anderson,

La maggior parte dei report raggiunge ora un data warehouse (che non è in realtà un DW, è essenzialmente una copia dello schema transazionale di produzione con un crapton di visualizzazioni e tabelle da altri database aggiunti). Avere rapporti che sono un giorno obsoleto è accettabile, ma sarebbe un vantaggio se potesse essere in diretta.
Paul Abbott,

Quanta sovrapposizione c'è nei campi? Dieci campi coprono tutti i 100 sottotipi o ci sono ~ 500 campi completamente distinti?
Jon of All Trades,

Ci sono 72 campi e 75 tipi di attività. 30 campi vengono utilizzati da una sola attività e la maggior parte del resto viene utilizzata da 5-10 attività. Ci sono una manciata di campi che vengono utilizzati da circa 30 diverse attività. Per la maggior parte, non c'è molta comunanza tra le attività.
Paul Abbott,

Risposte:


7

Quindi quello che sto cercando è un modo per archiviare un gran numero di attività che non hanno quasi campi in comune in modo da rendere più semplice la segnalazione.

Non abbastanza rappresentante per commentare prima, quindi eccoci qui!

Se lo scopo principale è la segnalazione e hai un DW (anche se non è uno schema a stella), ti consiglio di provare a inserirlo in uno schema a stella. I vantaggi sono query semplici e veloci. Il rovescio della medaglia è ETL, ma stai già considerando di spostare i dati in un nuovo design e ETL su uno schema a stella è probabilmente più semplice da costruire e mantenere rispetto a una soluzione wrapper XML (e SSIS è incluso nelle tue licenze di SQL Server). Inoltre, avvia il processo di progettazione di reportistica / analisi riconosciuta.

Quindi, come farlo ... Sembra che tu abbia quello che è noto come un fatto senza fatti . Questa è un'intersezione di attributi che definiscono un evento senza misura associata (come un prezzo di vendita). Hai delle date disponibili per alcune o tutte le tue attività? Probabilmente dovresti davvero avere un incrocio di un'attività, sito e data (e).

DimActivity- Immagino che ci sia uno schema, qualcosa che può permetterti di scomporli in almeno colonne relativamente condivise. Se è così, potresti averne tre? cinque? dimensioni per classi di attività. Nel peggiore dei casi hai un paio di colonne coerenti, come il nome dell'attività, puoi filtrare e lasci intestazioni generali come "Attributo1" ecc. Per i dettagli casuali rimanenti.

Non hai bisogno di tutto nella dimensione - (probabilmente) non dovrebbero esserci date nella dimensione Attività - dovrebbero essere tutti nel fatto, come Surrogate Key fa riferimento alla dimensione Data. Ad esempio, una Data che rimarrebbe nella dimensione di una persona sarebbe una data di nascita perché è un attributo di una persona. Una data di visita in ospedale risiederebbe in un dato di fatto, in quanto è un evento temporale associato ad una persona, tra le altre cose, ma non è un attributo della persona che visita l'ospedale. Altre discussioni sulla data nel fatto.

DimSite- Sembra semplice, quindi descriveremo le chiavi surrogate qui. In sostanza, si tratta solo di un ID univoco incrementale. La colonna Identità intera è comune. Ciò consente la separazione dei sistemi DW e di origine e garantisce unioni ottimali nel data warehouse. La chiave naturale o Business Key viene generalmente conservata, ma per manutenzione / progettazione non analisi e join. Schema di esempio:

CREATE TABLE [DIM].[Site]
(
 SiteSK INT NOT NULL IDENTITY PRIMARY KEY
,SiteNK INT NOT NULL --source system key
,SiteName VARCHAR(500) NOT NULL
)

DimDate- attributi data. Crea una "chiave intelligente" anziché un'identità. Ciò significa che è possibile digitare un numero intero significativo relativo a una data per query come WHERE DateSK = 20150708. Esistono molti script gratuiti per caricare DimDate e la maggior parte include questa chiave intelligente. ( un'opzione )

DimEmployee - il tuo XML lo ha incluso, se si tratta di una modifica più generale a DimPerson, e riempie con gli attributi della persona rilevante in quanto sono disponibili e pertinenti alla segnalazione.

E il tuo fatto è:

FactActivitySite
DimSiteSK - FK to DimSite
DimActivitySK - FK to DimActivity
DimEmployee - FK to DimEmployee
DimDateSK - FK to DimDate

Puoi rinominarli nel Fatto e puoi avere più chiavi di data per evento. I fatti sono in genere molto grandi, quindi evitare gli aggiornamenti è in genere buono ... se hai più aggiornamenti di data per un singolo evento, potresti provare un disegno Elimina / Inserisci aggiungendo un SK al fatto che consente la selezione di "aggiorna" le righe a essere eliminato quindi inserendo i dati più recenti.

Espandere le date Fact a tutto ciò che è necessario: StartDateSK, EndDateSK, ScheduledStartDateSK.

Tutte le dimensioni devono avere una riga sconosciuta, in genere con un -1 SK codificato. Quando carichi il fatto e un'attività non ha nessuna delle Date incluse, dovrebbe semplicemente caricare un -1.

Il fatto è una raccolta di riferimenti interi ai tuoi attributi memorizzati nelle dimensioni, uniscili e ottieni tutti i tuoi dettagli, in un modello di unione molto pulito, e il fatto, grazie ai suoi tipi di dati, è eccezionalmente piccolo e veloce. Poiché ti trovi in ​​SQL Server, aggiungi un indice columnstore per aumentare ulteriormente le prestazioni. Puoi semplicemente rilasciarlo e ricostruirlo durante ETL. Una volta arrivato a SQL 2014+ puoi scrivere negli indici columnstore.

inserisci qui la descrizione dell'immagine

Se vai su questa strada cerca la modellazione dimensionale. Consiglierei la metodologia Kimball . Ci sono anche molte guide gratuite là fuori, ma se si tratterà di qualcosa di diverso da una soluzione una tantum, è probabile che l'investimento valga la pena.


(domanda di wesdev): @Dave, quale strumento ERD hai usato?
ypercubeᵀᴹ

Ciò è stato fatto in Microsoft Visio 2013
Dave il
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.