Schlüsselwertpaare in der relationalen Datenbank


74

Hat jemand Erfahrung mit dem Speichern von Schlüssel-Wert-Paaren in einer Datenbank?

Ich habe diese Art von Tabelle verwendet:

CREATE TABLE key_value_pairs ( 
    itemid           varchar(32) NOT NULL,
    itemkey         varchar(32) NOT NULL,
    itemvalue       varchar(32) NOT NULL,
    CONSTRAINT ct_primarykey PRIMARY KEY(itemid,itemkey)
)

Dann könnten zum Beispiel die folgenden Zeilen existieren:

 itemid            itemkey        itemvalue    
 ----------------  -------------  ------------ 
 123               Colour         Red            
 123               Size           Medium             
 123               Fabric         Cotton

Das Problem bei diesem Schema ist, dass die zum Extrahieren von Daten erforderliche SQL-Syntax recht komplex ist. Wäre es besser, nur eine Reihe von Schlüssel- / Wertespalten zu erstellen?

CREATE TABLE key_value_pairs ( 
    itemid            varchar(32) NOT NULL,
    itemkey1        varchar(32) NOT NULL,
    itemvalue1      varchar(32) NOT NULL,
    itemkey2        varchar(32) NOT NULL,
    itemvalue2      varchar(32) NOT NULL,
 . . .etc . . .
)

Dies ist einfacher und schneller abzufragen, es fehlt jedoch die Erweiterbarkeit des ersten Ansatzes. Irgendein Rat?


Das Beispiel ist nicht optimal, da jedes Element wahrscheinlich nur eine Farbe, Größe und Stoff haben kann. In diesem Fall können Sie Spalten für Ihre Attribute verwenden.
Funkgesteuert

Antworten:


128

Bevor Sie Ihren Ansatz fortsetzen, würde ich Ihnen demütig vorschlagen, einen Schritt zurückzutreten und zu überlegen, ob Sie diese Daten wirklich in einer "Schlüssel-Wert-Paar" -Tabelle speichern möchten. Ich kenne Ihre Anwendung nicht, aber meine Erfahrung hat gezeigt, dass ich mir jedes Mal, wenn ich das getan habe, was Sie tun, später wünschte, ich hätte eine Farbtabelle, eine Stofftabelle und eine Größentabelle erstellt.

Denken Sie an referenzielle Integritätsbeschränkungen. Wenn Sie den Schlüssel-Wert-Paar-Ansatz verwenden, kann die Datenbank Ihnen nicht sagen, wann Sie versuchen, eine Farb-ID in einem Größenfeld zu speichern

Denken Sie an die Leistungsvorteile des Beitritts zu einer Tabelle mit 10 Werten im Vergleich zu einem generischen Wert, der möglicherweise Tausende von Werten in mehreren Domänen enthält. Wie nützlich wird ein Index für den Schlüsselwert wirklich sein?

Normalerweise liegt der Grund für das, was Sie tun, darin, dass die Domänen "benutzerdefinierbar" sein müssen. Wenn dies der Fall ist, werde auch ich Sie nicht dazu drängen, Tabellen im laufenden Betrieb zu erstellen (obwohl dies ein praktikabler Ansatz ist).

Wenn Sie jedoch der Meinung sind, dass die Verwaltung einfacher ist als bei mehreren Tabellen oder weil Sie sich eine Wartungsbenutzeroberfläche vorstellen, die für alle Domänen generisch ist, sollten Sie innehalten und gründlich überlegen, bevor Sie fortfahren.


11
A ++, das habe ich versucht zu sagen, aber du hast es viel besser artikuliert. Ich habe mehrere Schlüssel / Wert-Paartabellen in der Datenbank, mit der ich mich befasse, und ich bereue es jeden Tag. Jedes Mal, wenn sie fertig waren, weil "wir jetzt eine Lösung brauchen", und jedes Mal wusste ich, dass es das Falsche war.
Matthew Watson

Gibt es Datenspeichersysteme außerhalb von SQL, die KVP besser handhaben? Macht SQL es nicht gut, macht es niemand gut oder kann es nicht gut gemacht werden?
Federbrecher

@quillbreaker NoSql-Lösungen drehen sich häufig um die effiziente Speicherung von kv-Paaren.
Mavnn

Ich kann sehen, dass diese Antwort viele positive Stimmen hat. @ Daniel, was denkst du über den Abschnitt "Lektion 3: Offenes Schema" dieses Beitrags über reddit? Es scheint, dass reddit den Schlüssel / Wert-Ansatz verwendet und dies ist eine der Lektionen, die sie gelernt haben, als reddit wuchs. Vielen Dank.
Guido

3
@ Guido Für einige Systeme ist das relationale Modell nicht der beste Ansatz. Meine Antwort gilt nur, wenn Sie das relationale Modell zum Speichern Ihrer Daten ausgewählt haben.
Darrel Miller

17

Es gibt eine andere Lösung, die irgendwo zwischen den beiden liegt. Sie können eine Spalte vom Typ XML für die Schlüssel und Werte verwenden. Sie behalten also das Feld itemid bei und haben dann ein XML-Feld, das die für einige Schlüsselwertpaare definierte XML enthält. <items> <item key="colour" value="red"/><item key="xxx" value="blah"/></items> Wenn Sie dann Ihre Daten aus der Datenbank extrahieren, können Sie die XML auf verschiedene Arten verarbeiten. Abhängig von Ihrer Nutzung. Dies ist eine erweiterbare Lösung.


Das ist ein mögliches Szenario. Ich würde auch das einfache KV-Konzept bevorzugen. Trennt die Daten (Schlüssel, Wert) von ihren Metadaten (z. B. in einer XML-Konfiguration in einer separaten Spalte "Eigenschaften"). Flexibel, erweiterbar und einfach zu verarbeiten (zB JAXB). Sie müssen das DB-Schema nicht ständig ändern, wenn Sie Ihre Geschäftslogik erweitern. Die Persistenzlogik (Laden / Speichern) und die Schnittstelle zur Domäne können einmalig unter Verwendung von "Konvention über Konfiguration" entwickelt werden und müssen für Änderungen / Erweiterungen nicht berührt werden.
Michael Marton

Ich habe mit JSON gute Erfolge mit einem ähnlichen Ansatz erzielt. Der größte Nachteil ist, dass die Daten im Wertefeld in der SQL-Schicht nicht nützlich sind. Mit anderen Worten, verabschieden Sie sich von der Indizierung / Sortierung / Verknüpfung / Suche / Filterung der Daten im Wertefeld.
Rinogo

17

In den meisten Fällen würden Sie die erste Methode verwenden, weil Sie sich nicht wirklich hingesetzt und Ihr Modell durchdacht haben. "Nun, wir wissen noch nicht, wie die Schlüssel aussehen werden". Im Allgemeinen ist dies ein ziemlich schlechtes Design. Es wird langsamer sein, als Ihre Schlüssel tatsächlich als Spalten zu haben, was sie sein sollten.

Ich würde auch fragen, warum Ihre ID ein Varchar ist.

In dem seltenen Fall, dass Sie wirklich eine Schlüssel- / Wertetabelle implementieren müssen, ist die erste Lösung in Ordnung, obwohl ich die Schlüssel im Allgemeinen in einer separaten Tabelle haben möchte, damit Sie Varchars nicht als Schlüssel in Ihrem Schlüssel / speichern. Wertetabelle.

z.B,

CREATE TABLE valid_keys ( 
    id            NUMBER(10) NOT NULL,
    description   varchar(32) NOT NULL,
    CONSTRAINT pk_valid_keys PRIMARY KEY(id)
);

CREATE TABLE item_values ( 
    item_id NUMBER(10) NOT NULL,
    key_id  NUMBER(10) NOT NULL,
    item_value VARCHAR2(32) NOT NULL,
    CONSTRAINT pk_item_values PRIMARY KEY(item_id),
    CONSTRAINT fk_item_values_iv FOREIGN KEY (key_id) REFERENCES valid_keys (id)
);

Sie können dann sogar verrückt werden und den Schlüsseln einen "TYP" hinzufügen, um eine Typprüfung zu ermöglichen.


13

Ich habe einmal Schlüssel-Wert-Paare in einer Datenbank verwendet, um eine Tabelle (zur Dateneingabe) zu erstellen, in der ein Kassierer seine Aktivitäten aus der Arbeit an einer Kassenschublade zusammenfasst. Jedes k / v-Paar stellte eine benannte Zelle dar, in die der Benutzer einen Geldbetrag eingegeben hatte. Der Hauptgrund für diesen Ansatz ist, dass sich die Tabelle stark geändert hat. Neue Produkte und Dienstleistungen wurden routinemäßig hinzugefügt (daher erschienen neue Zellen). Außerdem wurden bestimmte Zellen in bestimmten Situationen nicht benötigt und konnten fallen gelassen werden.

Die App, die ich geschrieben habe, war eine Neufassung einer Anwendung, die das Kassiererblatt in separate Abschnitte unterteilt hat, die jeweils in einer anderen Tabelle dargestellt sind. Das Problem hierbei war, dass beim Hinzufügen von Produkten und Dienstleistungen Schemaänderungen erforderlich waren. Wie bei allen Designentscheidungen gibt es Vor- und Nachteile, eine bestimmte Richtung im Vergleich zu einer anderen einzuschlagen. Mein Redesign hat sicherlich langsamer und schneller Speicherplatz verbraucht. Es war jedoch sehr agil und ermöglichte das Hinzufügen neuer Produkte und Dienstleistungen innerhalb von Minuten. Das einzige Problem war jedoch der Festplattenverbrauch. Es gab keine anderen Kopfschmerzen, an die ich mich erinnern kann.

Wie bereits erwähnt, ist der Grund, warum ich normalerweise einen Schlüssel-Wert-Paar-Ansatz in Betracht ziehe, der, wenn Benutzer - dies könnte der Geschäftsinhaber sein - ihre eigenen Typen mit benutzerspezifischen Attributen erstellen möchten. In solchen Situationen bin ich zu folgender Feststellung gekommen.

Wenn entweder keine Daten über diese Attribute abgerufen werden müssen oder die Suche nach dem Abrufen eines Datenblocks auf die Anwendung verschoben werden kann, empfehle ich, alle Attribute in einem einzigen Textfeld zu speichern (mithilfe von JSON, YAML, XML usw.). ). Wenn es dringend erforderlich ist, Daten anhand dieser Attribute abzurufen, wird dies unübersichtlich.

Sie können eine einzelne "Attribut" -Tabelle (ID, Element-ID, Schlüssel, Wert, Datentyp, Sortierwert) erstellen, in der die Sortierspalte den tatsächlichen Wert in eine nach Zeichenfolgen sortierbare Darstellung umwandelt. (zB Datum: "2010-12-25 12:00:00", Nummer: "0000000001") Oder Sie können separate Attributtabellen nach Datentyp erstellen (z. B. string_attributes, date_attributes, number_attributes). Unter zahlreichen Vor- und Nachteilen beider Ansätze: Der erste ist einfacher, der zweite ist schneller. Beides führt dazu, dass Sie hässliche, komplexe Abfragen schreiben.


7

Aus Erfahrung habe ich festgestellt, dass bestimmte Schlüssel häufiger verwendet oder abgefragt werden. Normalerweise haben wir das Design dann leicht de-normalisiert, um ein bestimmtes Feld wieder in die Haupttabelle "item" aufzunehmen.

z.B. Wenn jedes Element eine Farbe hat, können Sie die Spalte Farbe zu Ihrer Elementtabelle hinzufügen. Stoff und Größe werden möglicherweise seltener verwendet und können in der Schlüssel-Wert-Paartabelle getrennt gehalten werden. Sie können die Farbe sogar in der Schlüssel-Wert-Paartabelle behalten, aber die Daten in der Elementtabelle duplizieren, um die Leistungsvorteile zu erzielen.

Dies hängt natürlich von den Daten ab und davon, wie flexibel die Schlüssel-Wert-Paare sein müssen. Dies kann auch dazu führen, dass Ihre Attributdaten nicht konsistent gefunden werden. Durch die De-Normalisierung werden die Abfragen jedoch erheblich vereinfacht und ihre Leistung verbessert.

Normalerweise würde ich eine De-Normalisierung nur in Betracht ziehen, wenn die Leistung zu einem Problem wird, und nicht nur, um eine Abfrage zu vereinfachen.


6

PostgreSQL 8.4 unterstützt den Datentyp hstore zum Speichern von Sätzen von (Schlüssel-, Wert-) Paaren in einem einzelnen PostgreSQL-Datenfeld. Informationen zur Verwendung finden Sie unter http://www.postgresql.org/docs/8.4/static/hstore.html . Obwohl es eine sehr alte Frage ist, dachte ich, diese Informationen weiterzugeben, weil ich dachte, sie könnten jemandem helfen.


3

Ich denke, der beste Weg, solche Tabellen zu entwerfen, ist wie folgt:

  • Machen Sie die häufig verwendeten Felder als Spalten in der Datenbank.
  • Stellen Sie eine Misc-Spalte bereit, die ein Wörterbuch (in JSON / XML / anderem Zeichenfolgenformat) enthält, das die Felder als Schlüssel-Wert-Paare enthält.

Wichtige Punkte:

  • In den meisten Situationen können Sie Ihre normalen SQL-Abfragen schreiben, um SQL abzufragen.
  • Sie können eine FullTextSearch für die Schlüssel-Wert-Paare durchführen. MySQL hat eine Volltextsuchmaschine, ansonsten können Sie "Like" -Abfragen verwenden, die etwas langsamer sind. Während die Volltextsuche schlecht ist, gehen wir davon aus, dass solche Abfragen weniger sind, sodass nicht zu viele Probleme auftreten sollten.
  • Wenn Ihre Schlüssel-Wert-Paare einfache boolesche Flags sind, hat diese Technik die gleiche Leistung wie eine separate Spalte für den Schlüssel. Jede komplexere Operation an den Schlüsselwertpaaren sollte außerhalb der Datenbank durchgeführt werden.
  • Wenn Sie sich die Häufigkeit von Abfragen über einen bestimmten Zeitraum ansehen, erfahren Sie, welche Schlüssel-Wert-Paare in Spalten konvertiert werden müssen.
  • Diese Technik macht es auch einfach, Integritätsbeschränkungen für die Datenbank zu erzwingen.
  • Es bietet Entwicklern einen natürlicheren Weg, um ihr Schema und ihren Code neu zu faktorisieren.

2

Ich verstehe nicht, warum das SQL zum Extrahieren von Daten für Ihr erstes Design komplex sein sollte. Um alle Werte für einen Artikel zu erhalten, gehen Sie einfach wie folgt vor:

SELECT itemkey,itemvalue FROM key_value_pairs WHERE itemid='123';

oder wenn Sie nur einen bestimmten Schlüssel für diesen Artikel möchten:

SELECT itemvalue FROM key_value_pairs WHERE itemid='123' AND itemkey='Fabric';

Das erste Design bietet Ihnen auch die Flexibilität, jederzeit neue Schlüssel hinzuzufügen.


1
Es wird kompliziert, wenn einer der Werte ein Datum ist und Sie zwischen Datumsangaben nach bestimmten Schlüsseln suchen möchten.
Arthur Thomas

4
Stellen Sie sich die Abfrage in umgekehrter Reihenfolge vor: Suchen Sie die Element-ID für eine Reihe von Schlüssel / Wert-Paaren. Dies erfordert eine kaskadierende Reihe von Verknüpfungen. Es wird weiter durch die Notwendigkeit erschwert, die Auswahl einer Obermenge zu vermeiden; Beispiel: find (Farbe = Rot, Größe = Mittel) darf die ID 123 nicht zurückgeben, da dieses Set eine weitere Zeile enthält (Stoff = Baumwolle)
horace

1

Die erste Methode ist ganz ok. Sie können eine UDF erstellen, die die gewünschten Daten extrahiert, und diese einfach aufrufen.


1

Wenn Sie nur sehr wenige mögliche Schlüssel haben, würde ich sie nur als Spalten speichern. Wenn der Satz möglicher Schlüssel jedoch groß ist, ist Ihr erster Ansatz gut (und der zweite Ansatz wäre unmöglich).

Oder ist es so, dass jeder Gegenstand nur eine endliche Anzahl von Schlüsseln haben kann, aber die Schlüssel könnten etwas aus einem großen Satz sein?

Sie können auch einen Object Relational Mapper verwenden, um die Abfrage zu vereinfachen.


Das ORM erleichtert das Abfragen, verbessert jedoch nicht die Leistung. Eine handcodierte SQL-Abfrage bietet möglicherweise eine bessere Leistung.
Mansu

Es könnte. Aber wahrscheinlich nicht und Geschwindigkeit war nicht etwas, worüber er fragte.
Hannes Ovrén

1

Die erste Methode ist zu den von Ihnen genannten Kosten viel flexibler.

Und der zweite Ansatz ist niemals realisierbar, wie Sie gezeigt haben. Stattdessen würden Sie dies tun (gemäß Ihrem ersten Beispiel).

create table item_config (item_id int, colour varchar, size varchar, fabric varchar)

Dies funktioniert natürlich nur, wenn die Datenmenge bekannt ist und sich nicht viel ändert.

In der Regel sollte jeder Anwendung, die das Ändern der DDL von Tabellen für normale Arbeiten erfordert, ein zweiter und ein dritter Gedanke gegeben werden.


1

Ein Verstoß gegen die Normalisierungsregeln ist in Ordnung, solange die Geschäftsanforderungen noch erfüllt werden können. Haben key_1, value_1, key_2, value_2, ... key_n, value_nkann in Ordnung sein, bis zu dem Punkt, den Sie brauchen key_n+1, value_n+1.

Meine Lösung war eine Datentabelle für gemeinsam genutzte Attribute und XML für eindeutige Attribute. Das heißt, ich benutze beide. Wenn alles (oder die meisten Dinge) eine Größe haben, ist Größe eine Spalte in der Tabelle. Wenn nur Objekt A das Attribut Z hat, wird Z als XML gespeichert, ähnlich der bereits gegebenen Antwort von Peter Marshall.


Ein Verstoß gegen die Normalisierungsregeln ist NICHT in Ordnung, solange die Geschäftsanforderungen noch erfüllt werden können. Ein Verstoß gegen Normalisierungsregeln ist in Ordnung, solange normalisierte Daten nicht performant sind. Selbst dann möchten Sie wirklich eine normalisierte Version der Daten und eine normalisierte materialisierte Ansicht.
Matthew Watson

Die Lösung key_n, value_n macht den SQl wirklich sehr, sehr schwer. Wie würden Sie den Salz für "Stoff = 'Baumwolle' und Farbe = 'Rot' codieren? Am Ende hätten Sie: wo (key_1 =" Stoff "und value_1 =" Baumwolle "oder key_2 =" Stoff "und Wert_1 =" Baumwolle " ".... und (...
James Anderson

0

Die zweite Tabelle ist stark de-normalisiert. Ich würde beim ersten Ansatz bleiben.


0

Ich denke, Sie tun das Richtige, solange sich die Schlüssel / Werte für einen bestimmten Elementtyp häufig ändern.
Wenn sie eher statisch sind, ist es sinnvoller, die Artikeltabelle einfach breiter zu machen.

Wir verwenden einen ähnlichen (aber etwas komplexeren) Ansatz mit viel Logik um die Schlüssel / Werte sowie Tabellen für die für jeden Schlüssel zulässigen Wertetypen.
Auf diese Weise können wir Elemente als eine weitere Instanz eines Schlüssels definieren, und unsere zentrale Tabelle ordnet beliebige Schlüsseltypen anderen beliebigen Schlüsseltypen zu. Es kann Ihr Gehirn schnell in Knoten binden, aber sobald Sie die Logik geschrieben und gekapselt haben, um alles zu handhaben, haben Sie viel Flexibilität.

Ich kann bei Bedarf weitere Einzelheiten darüber schreiben, was wir tun.


0

Wenn die Schlüssel dynamisch sind oder viele davon vorhanden sind, verwenden Sie die Zuordnungstabelle, die Sie als erstes Beispiel haben. Darüber hinaus ist dies die allgemeinste Lösung. Sie lässt sich in Zukunft am besten skalieren, wenn Sie mehr Schlüssel hinzufügen. Es ist einfach, SQL zu codieren, um die Daten herauszuholen, und die Datenbank kann die Abfrage besser optimieren, als Sie sich vorstellen ( Das heißt, ich würde mich nicht bemühen, diesen Fall vorzeitig zu optimieren, es sei denn, es würde sich später als Engpass beim Testen herausstellen. In diesem Fall könnten Sie die nächsten beiden Optionen in Betracht ziehen.

Wenn die Schlüssel eine bekannte Menge sind und es nicht viele davon gibt (<10, vielleicht <5), sehe ich das Problem nicht darin, sie als Wertespalten auf dem Element zu haben.

Wenn es eine mittlere Anzahl bekannter fester Schlüssel gibt (10 - 30), haben Sie möglicherweise eine andere Tabelle, in der die item_details gespeichert sind.

Ich sehe jedoch nie die Notwendigkeit, Ihre zweite Beispielstruktur zu verwenden, sie sieht umständlich aus.


0

Wenn Sie den Weg einer KVP-Tabelle gehen und ich muss sagen, dass mir diese Technik selbst überhaupt nicht gefällt, da es in der Tat schwierig ist, sie abzufragen, sollten Sie in Betracht ziehen, die Werte für eine einzelne Element-ID mithilfe einer geeigneten Technik zu gruppieren für welche Plattform auch immer Sie sich befinden.

RDBMS neigen dazu, Zeilen zu verteilen, um Blockkonflikte bei Einfügungen zu vermeiden. Wenn Sie 8 Zeilen abrufen müssen, können Sie leicht auf 8 Blöcke der Tabelle zugreifen, um sie zu lesen. Unter Oracle sollten Sie einen Hash-Cluster zum Speichern dieser in Betracht ziehen, der die Leistung beim Zugriff auf die Werte für eine bestimmte Element-ID erheblich verbessern würde.


0

Ihr Beispiel ist kein sehr gutes Beispiel für die Verwendung von Schlüsselwertpaaren. Ein besseres Beispiel wäre die Verwendung einer Gebührentabelle, einer Kundentabelle und einer Customer_Fee-Tabelle in einer Abrechnungsanwendung. Die Gebührentabelle würde aus Feldern bestehen wie: fee_id, fee_name, fee_description Die Customer_Fee-Tabelle würde aus Feldern bestehen wie: customer_id, fee_id, fee_value


0

Die Zeiten haben sich geändert. Jetzt haben Sie andere Datenbanktypen, die Sie neben relationalen Datenbanken verwenden können. Zu den NOSQL-Optionen gehören jetzt Spaltenspeicher, Dokumentenspeicher, Diagramme und Multi-Modelle (siehe: http://en.wikipedia.org/wiki/NoSQL ).

Bei Schlüsselwertdatenbanken können Sie unter anderem CouchDb, Redis und MongoDB auswählen (ohne darauf beschränkt zu sein).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.