Dati sparsi: archiviazione e recupero efficienti in un RDBMS
-
03-07-2019 - |
Domanda
Ho una tabella che rappresenta i valori delle metriche del file sorgente attraverso le revisioni del progetto, come il seguente:
Revision FileA FileB FileC FileD FileE ...
1 45 3 12 123 124
2 45 3 12 123 124
3 45 3 12 123 124
4 48 3 12 123 124
5 48 3 12 123 124
6 48 3 12 123 124
7 48 15 12 123 124
(La vista relazionale dei dati sopra è diversa. Ogni riga contiene le seguenti colonne: Revisione, FileId, Valore. I file e le loro revisioni da cui vengono calcolati i dati sono memorizzati nei repository Subversion, quindi stiamo provando a rappresenta la struttura del repository in uno schema relazionale.)
Possono esserci fino a 23750 file in 10000 revisioni (questo è il caso del ImageMagick programma di disegno). Come puoi vedere, la maggior parte dei valori sono gli stessi tra le revisioni successive, quindi i dati utili della tabella sono piuttosto scarsi. Sto cercando un modo per archiviare i dati che
- evita la replica e utilizza lo spazio in modo efficiente (attualmente la rappresentazione non sparsa richiede 260 GB (dati + indice) per meno del 10% dei dati che voglio archiviare)
- mi consente di recuperare in modo efficiente i valori per una revisione specifica utilizzando una query SQL (senza scorrere esplicitamente le revisioni o i file)
- mi consente di recuperare in modo efficiente la revisione per un valore di metrica specifico.
Idealmente, la soluzione non dovrebbe dipendere da un particolare RDBMS e dovrebbe essere compatibile con < a href = "http://en.wikipedia.org/wiki/Hibernate_%28Java%29" rel = "nofollow noreferrer"> Hibernate . Se ciò non è possibile, posso convivere con le funzionalità specifiche di Hibernate, MySQL o PostgreSQL.
Soluzione
Ecco come potrei modellarlo. Ho tralasciato la tabella Revisioni e la tabella File poiché dovrebbero essere abbastanza autoesplicative.
CREATE TABLE Revision_Files
(
start_revision_number INT NOT NULL,
end_revision_number INT NOT NULL,
file_number INT NOT NULL,
value INT NOT NULL,
CONSTRAINT PK_Revision_Files PRIMARY KEY CLUSTERED (start_revision_number, file_number),
CONSTRAINT CHK_Revision_Files_start_before_end CHECK (start_revision_number <= end_revision_number)
)
GO
Per ottenere tutti i valori per i file di una particolare revisione è possibile utilizzare la seguente query. Partecipare alla tabella dei file con un join esterno ti permetterebbe di ottenere quelli che non hanno un valore definito per quella revisione.
SELECT
REV.revision_number,
RF.file_number,
RF.value
FROM
Revisions REV
INNER JOIN Revision_Files RF ON
RF.start_revision_number <= REV.revision_number AND
RF.end_revision_number >= REV.revision_number
GO
Supponendo che io capisca correttamente cosa vuoi nel tuo terzo punto, questo ti permetterà di ottenere tutte le revisioni per le quali un determinato file ha un certo valore:
SELECT
REV.revision_number
FROM
Revision_Files RF
INNER JOIN Revisions REV ON
REV.revision_number BETWEEN RF.start_revision_number AND RF.end_revision_number
WHERE
RF.file_number = @file_number AND
RF.value = @value
GO