it-swarm.com.de

Ist es jemals in Ordnung, Listen in einer relationalen Datenbank zu verwenden?

Ich habe versucht, eine Datenbank zu entwerfen, die zu einem Projektkonzept passt, und bin auf ein heiß diskutiertes Problem gestoßen. Ich habe ein paar Artikel und einige Antworten zum Stapelüberlauf gelesen, die besagen, dass es niemals (oder fast nie) in Ordnung ist, eine Liste von IDs oder Ähnlichem in einem Feld zu speichern - alle Daten sollten relational sein usw.

Das Problem, auf das ich stoße, ist jedoch, dass ich versuche, einen Aufgabenzuweiser zu erstellen. Personen erstellen Aufgaben, weisen sie mehreren Personen zu und speichern sie in der Datenbank.

Wenn ich diese Aufgaben einzeln in "Person" speichere, muss ich natürlich Dutzende von Dummy-Spalten "TaskID" haben und sie mikroverwalten, da einer Person beispielsweise 0 bis 100 Aufgaben zugewiesen werden können.

Wenn ich die Aufgaben in einer "Aufgaben" -Tabelle speichere, muss ich Dutzende von Dummy-Spalten "PersonID" haben und sie mikroverwalten - dasselbe Problem wie zuvor.

Ist es für ein Problem wie dieses in Ordnung, eine Liste von IDs in der einen oder anderen Form zu speichern, oder denke ich einfach nicht an einen anderen Weg, wie dies erreicht werden kann, ohne die Prinzipien zu brechen?

95
linus72982

Das Schlüsselwort und das Schlüsselkonzept, das Sie untersuchen müssen, ist Datenbank Normalisierung .

Anstatt Informationen zu den Zuordnungen zu den Personen- oder Aufgabentabellen hinzuzufügen, fügen Sie eine neue Tabelle mit diesen Zuordnungsinformationen und relevanten Beziehungen hinzu.

Beispiel: Sie haben folgende Tabellen:

Personen:

 + −−−− + −−−−−−−−−−− + 
 | ID | Name | 
 + ==== + =========== + 
 | 1 | Alfred | 
 | 2 | Jebediah | 
 | 3 | Jacob | 
 | 4 | Hesekiel | 
 + −−−− + −−−−−−−−−−− + 

Aufgaben:

 + −−−− + −−−−−−−−−−−−−−−−−−−−−− 
 | ID | Name | 
 + ==== + =================== + 
 | 1 | Füttere die Hühner | 
 | 2 | Pflug | 
 | 3 | Milchkühe | 
 | 4 | Heben Sie eine Scheune | 
 + −−−− + −−−−−−−−−−−−−−−−−−−− 

Sie würden dann eine dritte Tabelle mit Zuweisungen erstellen. Diese Tabelle würde die Beziehung zwischen den Personen und den Aufgaben modellieren:

 + −−−− + −−−−−−−−−−− + −−−−−−−−− + 
 | ID | PersonId | TaskId | 
 + ==== + =========== + ========= + 
 | 1 | 1 | 3 | 
 | 2 | 3 | 2 | 
 | 3 | 2 | 1 | 
 | 4 | 1 | 4 | 
 + −−−− + −−−−−−−−−−− + −−−−−−−−− + 

Wir hätten dann eine Fremdschlüsseleinschränkung, sodass die Datenbank erzwingt, dass die PersonId und die TaskIds gültige IDs für diese Fremdelemente sein müssen. In der ersten Zeile sehen wir PersonId is 1, Also ist AlfredTaskId 3, Milchkühe zugeordnet.

Was Sie hier sehen sollten, ist, dass Sie so wenige oder so viele Aufgaben pro Aufgabe oder pro Person haben können, wie Sie möchten. In diesem Beispiel werden Hesekiel Keine Aufgaben zugewiesen, und Alfred wird 2 zugewiesen. Wenn Sie eine Aufgabe mit 100 Personen haben, führen Sie SELECT PersonId from Assignments WHERE TaskId=<whatever>; ergibt 100 Zeilen mit verschiedenen zugewiesenen Personen. Sie können WHERE auf der PersonId finden, um alle dieser Person zugewiesenen Aufgaben zu finden.

Wenn Sie Abfragen zurückgeben möchten, bei denen die IDs durch die Namen und Aufgaben ersetzt werden, erfahren Sie, wie Sie Tabellen verbinden.

249
whatsisname

Sie stellen hier zwei Fragen.

Zunächst fragen Sie, ob es in Ordnung ist, in einer Spalte serialisierte Listen zu speichern. Ja es ist gut. Wenn Ihr Projekt dies erfordert. Ein Beispiel könnten Produktbestandteile für eine Katalogseite sein, auf der Sie nicht versuchen möchten, jeden Inhaltsstoff einzeln zu verfolgen.

Leider beschreibt Ihre zweite Frage ein Szenario, in dem Sie sich für einen relationaleren Ansatz entscheiden sollten. Sie benötigen 3 Tische. Eine für die Personen, eine für die Aufgaben und eine, die die Liste der Aufgaben verwaltet, die welchen Personen zugewiesen sind. Das letzte ist vertikal, eine Zeile pro Person/Aufgaben-Kombination mit Spalten für Ihren Primärschlüssel, Ihre Aufgaben-ID und Ihre Personen-ID.

35
GrandmasterB

Was Sie beschreiben, wird als "viele zu viele" -Beziehung bezeichnet, in Ihrem Fall zwischen Person und Task. Es wird normalerweise mithilfe einer dritten Tabelle implementiert, die manchmal als "Link" - oder "Querverweis" -Tabelle bezeichnet wird. Zum Beispiel:

create table person (
    person_id integer primary key,
    ...
);

create table task (
    task_id integer primary key,
    ...
);

create table person_task_xref (
    person_id integer not null,
    task_id integer not null,
    primary key (person_id, task_id),
    foreign key (person_id) references person (person_id),
    foreign key (task_id) references task (task_id)
);
21
Mike Partridge

... es ist nie (oder fast nie) in Ordnung, eine Liste von IDs oder Ähnlichem in einem Feld zu speichern

Das einzige Mal, dass Sie mehr als ein Datenelement in einem einzelnen Feld speichern können, ist, wenn dieses Feld nur ist wird jemals als einzelne Einheit verwendet und wird nie als aus diesen kleineren Elementen zusammengesetzt betrachtet . Ein Beispiel könnte ein Bild sein, das in einem BLOB-Feld gespeichert ist. Es besteht aus vielen, vielen kleineren Elementen (Bytes), aber diese bedeuten nichts für die Datenbank und können nur alle zusammen verwendet werden (und sehen hübsch aus an einen Endbenutzer).

Da eine "Liste" per Definition aus kleineren Elementen (Elementen) besteht, ist dies hier nicht der Fall und Sie sollten die Daten normalisieren.

... wenn ich diese Aufgaben einzeln in "Person" speichere, muss ich Dutzende von Dummy-Spalten "TaskID" haben ...

Nein. Sie haben einige Zeilen in einer Schnittpunkttabelle (a.k.a. Schwache Entität) zwischen Person und Aufgabe. Datenbanken können sehr gut mit vielen Zeilen arbeiten. Sie sind eigentlich ziemlich blöd darin, mit vielen [wiederholten] Spalten zu arbeiten.

Schönes klares Beispiel von Whatsisname.

13
Phill W.

In bestimmten vorberechneten Feldern kann dies legitim sein.

Wenn einige Ihrer Abfragen teuer sind und Sie sich dafür entscheiden, vorberechnete Felder zu verwenden, die automatisch mithilfe von Datenbank-Triggern aktualisiert werden, ist es möglicherweise legitim, die Listen in einer Spalte zu belassen.

In der Benutzeroberfläche möchten Sie diese Liste beispielsweise mithilfe der Rasteransicht anzeigen, in der jede Zeile nach einem Doppelklick vollständige Details (mit vollständigen Listen) öffnen kann:

REGISTERED USER LIST
+------------------+----------------------------------------------------+
|Name              |Top 3 most visited tags                             |
+==================+====================================================+
|Peter             |Design, Fitness, Gifts                              |
+------------------+----------------------------------------------------+
|Lucy              |Fashion, Gifts, Lifestyle                           |
+------------------+----------------------------------------------------+

Sie halten die zweite Spalte nach Auslöser aktualisiert, wenn der Kunde einen neuen Artikel besucht, oder nach geplanter Aufgabe.

Sie können ein solches Feld auch für die Suche zur Verfügung stellen (als normaler Text).

In solchen Fällen ist es legitim, Listen zu führen. Sie müssen nur den Fall berücksichtigen, dass die maximale Feldlänge möglicherweise überschritten wird.


Wenn Sie Microsoft Access verwenden, sind angebotene mehrwertige Felder ein weiterer spezieller Anwendungsfall. Sie behandeln Ihre Listen in einem Feld automatisch.

Sie können jedoch jederzeit auf die in anderen Antworten angegebene normalisierte Standardform zurückgreifen.


Zusammenfassung: Normale Formen der Datenbank sind theoretische Modelle, die zum Verständnis wichtiger Aspekte der Datenmodellierung erforderlich sind. Bei der Normalisierung werden jedoch weder die Leistung noch andere Kosten für das Abrufen der Daten berücksichtigt. Es liegt außerhalb des Rahmens dieses theoretischen Modells. Das Speichern von Listen oder anderen vorberechneten (und kontrollierten) Duplikaten ist jedoch häufig für die praktische Implementierung erforderlich.

Würden wir in Anbetracht der obigen Ausführungen in der praktischen Implementierung eine Abfrage bevorzugen, die sich auf eine perfekte Normalform stützt und 20 Sekunden ausführt, oder eine gleichwertige Abfrage, die sich auf vorberechnete Werte stützt, die 0,08 s dauert? Niemand mag es, wenn sein Softwareprodukt der Langsamkeit beschuldigt wird.

4
miroxlav

Gegeben zwei Tabellen; Wir nennen sie Person und Aufgabe, jede mit ihrer eigenen ID (PersonID, TaskID). Die Grundidee besteht darin, eine dritte Tabelle zu erstellen, um sie miteinander zu verbinden. Wir werden diese Tabelle PersonToTask nennen. Zumindest sollte es eine eigene ID haben, ebenso wie die beiden anderen. Wenn es darum geht, jemanden einer Aufgabe zuzuweisen; Sie müssen die Personentabelle nicht mehr AKTUALISIEREN, sondern müssen lediglich eine neue Zeile in die PersonToTaskTable einfügen. Und die Wartung wird einfacher - das Löschen einer Aufgabe wird nur zu einem DELETE basierend auf der TaskID, ohne dass die Personentabelle und die damit verbundene Analyse mehr aktualisiert werden müssen

CREATE TABLE dbo.PersonToTask (
    pttID INT IDENTITY(1,1) NOT NULL,
    PersonID INT NULL,
    TaskID   INT NULL
)

CREATE PROCEDURE dbo.Task_Assigned (@PersonID INT, @TaskID INT)
AS
BEGIN
    INSERT PersonToTask (PersonID, TaskID)
    VALUES (@PersonID, @TaskID)
END

CREATE PROCEDURE dbo.Task_Deleted (@TaskID INT)
AS
BEGIN
    DELETE PersonToTask  WHERE TaskID = @TaskID
    DELETE Task          WHERE TaskID = @TaskID
END

Wie wäre es mit einem einfachen Bericht oder wer ist einer Aufgabe zugeordnet?

CREATE PROCEDURE dbo.Task_CurrentAssigned (@TaskID INT)
AS
BEGIN
    SELECT PersonName
    FROM   dbo.Person
    WHERE  PersonID IN (SELECT PersonID FROM dbo.PersonToTask WHERE TaskID = @TaskID)
END

Sie könnten natürlich noch viel mehr tun; Ein TimeReport kann durchgeführt werden, wenn Sie DateTime-Felder für TaskAssigned und TaskCompleted hinzugefügt haben. Es liegt alles bei dir

0
Mad Myche

Es kann funktionieren, wenn Sie beispielsweise über von Menschen lesbare Primärschlüssel verfügen und eine Liste von Aufgaben möchten, ohne sich mit der vertikalen Natur einer Tabellenstruktur befassen zu müssen. viel einfacher, die erste Tabelle zu lesen.

------------------------  
Employee Name | Task 
Jack          |  1,2,5
Jill          |  4,6,7
------------------------

------------------------  
Employee Name | Task 
Jack          |  1
Jack          |  2
Jack          |  5
Jill          |  4
Jill          |  6
Jill          |  7
------------------------

Die Frage wäre dann: Sollte die Aufgabenliste bei Bedarf gespeichert oder generiert werden, was weitgehend von Anforderungen abhängt, wie z. B.: Wie oft die Liste benötigt wird, wie genau wie viele Datenzeilen vorhanden sind, wie die Daten verwendet werden usw. .. Danach sollte eine Analyse der Kompromisse mit der Benutzererfahrung und der Erfüllung der Anforderungen durchgeführt werden.

Vergleichen Sie beispielsweise die Zeit, die zum Abrufen der beiden Zeilen benötigt wird, mit dem Ausführen einer Abfrage, die die beiden Zeilen generiert. Wenn es lange dauert und der Benutzer nicht die aktuellste Liste benötigt (* erwartet weniger als 1 Änderung pro Tag), kann sie gespeichert werden.

Wenn der Benutzer eine historische Aufzeichnung der ihm zugewiesenen Aufgaben benötigt, ist es auch sinnvoll, wenn die Liste gespeichert wird. Es kommt also wirklich darauf an, was Sie tun, sagen Sie niemals nie.

0
Double E CPU

Sie nehmen einen anderen Tisch, drehen ihn um 90 Grad und schieben ihn in einen anderen Tisch.

Es ist wie in einer Auftragstabelle, in der Sie itemProdcode1, itemQuantity1, itemPrice1 ... itemProdcode37, itemQuantity37, itemPrice37 haben. Abgesehen davon, dass es umständlich ist, programmgesteuert umzugehen, können Sie garantieren, dass morgen jemand 38 Dinge bestellen möchte.

Ich würde es nur auf Ihre Weise tun, wenn die 'Liste' nicht wirklich eine Liste ist, d. H. Wo sie als Ganzes steht und jede einzelne Werbebuchung nicht auf eine klare und unabhängige Entität verweist. In diesem Fall füllen Sie einfach alles in einen Datentyp, der groß genug ist.

Eine Bestellung ist also eine Liste, eine Stückliste ist eine Liste (oder eine Liste von Listen, deren Implementierung "seitwärts" noch mehr ein Albtraum wäre). Aber eine Notiz/ein Kommentar und ein Gedicht sind es nicht.

0

Wenn es "nicht in Ordnung" ist, ist es ziemlich schlimm, dass jede Wordpress Site hat jemals eine Liste in wp_usermeta mit wp_capabilities in einer Zeile, entlassen_wp_pointers Liste in einer Zeile und andere ...

In solchen Fällen ist es möglicherweise besser für die Geschwindigkeit, da Sie fast immer die Liste möchten. Aber Wordpress ist nicht als perfektes Beispiel für Best Practices bekannt.

0
NoBugs