Ist es sinnvoll, alle Spalten bis auf eine als Primärschlüssel zu markieren?


9

Ich habe eine Tabelle mit Filmen. Die Felder sind :
id (PK), title, genre, runtime, released_in, tags, origin, downloads.

Meine Datenbank kann nicht durch doppelte Zeilen verschmutzt werden, daher möchte ich die Eindeutigkeit erzwingen. Das Problem ist, dass verschiedene Filme den gleichen Titel oder sogar die gleichen Felder außer tagsund haben können downloads. Wie kann man die Einzigartigkeit erzwingen?

Ich dachte an zwei Möglichkeiten:

  • Machen Sie alle Felder außer dem downloadsPrimärschlüssel. Ich halte mich zurück, downloadsda es JSON ist und es wahrscheinlich die Leistung beeinflussen wird.
  • Nur idals Primärschlüssel behalten , aber mit allen anderen Spalten eine eindeutige Einschränkung hinzufügen (außer wieder downloads).

Ich habe diese Frage gelesen , die sehr ähnlich ist, aber ich habe nicht ganz verstanden, was ich tun soll. Derzeit ist diese Tabelle nicht mit anderen Tabellen verwandt, könnte aber in Zukunft sein.

Im Moment habe ich etwas weniger als 20.000 Datensätze, aber ich erwarte, dass die Zahl wächst. Ich weiß nicht, ob dies für das Thema relevant ist.

BEARBEITEN: Ich habe das Schema geändert und hier ist, wie ich die Tabelle erstellen würde:

CREATE TABLE movies (
    id          serial PRIMARY KEY,
    title       text NOT NULL,
    runtime     smallint NOT NULL CHECK (runtime >= 0),
    released_in smallint NOT NULL CHECK (released_in > 0),
    genres      text[] NOT NULL default ARRAY[]::text[],
    tags        text[] NOT NULL default ARRAY[]::text[],
    origin      text[] NOT NULL default ARRAY[]::text[],
    downloads   json NOT NULL,
    inserted_at timestamp NOT NULL default current_timestamp,
    CONSTRAINT must_be_unique UNIQUE(title,runtime,released_in,genres,tags,origin)
);

Ich habe auch die timestampSpalte hinzugefügt , aber das ist kein Problem, da ich sie nicht berühren werde. So wird es immer automatisch und einzigartig sein.


Antworten:


4

Ihre Tabellendefinition sieht jetzt überall vernünftig aus. Bei allen Spalten funktioniert NOT NULLdie UNIQUEEinschränkung wie erwartet - mit Ausnahme von Tippfehlern und geringfügigen Unterschieden in der Rechtschreibung, die meiner Meinung nach häufig vorkommen. Betrachten Sie den Kommentar von @ a_horse .

Alternative mit funktionalem eindeutigem Index

Die andere Option wäre ein funktionaler eindeutiger Index (ähnlich dem, was @ Dave kommentiert hat ). Ich würde jedoch einen uuidDatentyp verwenden, um die Indexgröße und -leistung zu optimieren.

Die Umwandlung von Array in Text erfolgt nicht IMMUTABLE(aufgrund der generischen Implementierung):

Daher benötigen Sie eine kleine Hilfsfunktion , um sie für unveränderlich zu erklären :

CREATE OR REPLACE FUNCTION f_movie_uuid(_title text
                                      , _runtime int2
                                      , _released_in int2
                                      , _genres text[]
                                      , _tags text[]
                                      , _origin text[])
  RETURNS uuid LANGUAGE sql IMMUTABLE AS  -- faking IMMUTABLE
'SELECT md5(_title || _runtime::text || _released_in::text
         || _genres::text || _tags::text || _origin::text)::uuid';

Verwenden Sie es für die Indexdefinition:

CREATE UNIQUE INDEX movies_uni_idx
ON movies (f_movie_uuid(title,runtime,released_in,genres,tags,origin));

SQL Fiddle.

Mehr Details:

Sie könnten die generierte UUID als PK verwenden, aber ich würde immer noch die serialSpalte mit ihren 4 Bytes verwenden, was für FK-Referenzen und andere Zwecke einfach und billig ist. Eine UUID wäre eine großartige Option für verteilte Systeme, die unabhängig PK-Werte generieren müssen. Oder für sehr große Tische, aber dafür gibt es in unserem Sonnensystem bei weitem nicht genug Filme.

Vor-und Nachteile

Eine eindeutige Einschränkung wird mit einem eindeutigen Index für die beteiligten Spalten implementiert. Fügen Sie relevante Spalten zuerst in die Einschränkungsdefinition ein, und Sie haben einen nützlichen Index für andere Zwecke als Sicherheitenvorteil.

Es gibt andere spezifische Vorteile, hier ist eine Liste:

Der funktionale eindeutige Index ist (möglicherweise viel) kleiner, was ihn wesentlich schneller machen kann. Wenn Ihre Spalten nicht zu groß sind, ist der Unterschied nicht groß. Es gibt auch die geringen Gemeinkosten für die Berechnung.

Das Verketten aller Spalten kann zu Fehlalarmen führen (dies 'foo ' || 'bar' = 'foob ' || 'ar'ist jedoch in diesem Fall sehr unwahrscheinlich. Tippfehler sind so viel wahrscheinlicher, dass Sie sie hier ignorieren können.

Einzigartigkeit und Arrays

Arrays müssten konsistent sortiert werden , um in jeder einzigartigen Anordnung, die sich auf den =Bediener stützt, Sinn zu machen, weil '{1,2}' <> '{2,1}'. Ich schlage vor , Look-up - Tabellen für genre, tagund originmit serialPK und eindeutigen Einträgen, die für Array - Elemente Fuzzy - Suche ermöglichen. Dann:

In beiden Fällen kann die Suche mit Arrays direkt oder mit einem normalisierten Schema und einer materialisierten Ansicht mit dem richtigen Index und den richtigen Operatoren sehr effizient sein:

Beiseite

Wenn Sie Postgres 9.4 oder höher verwenden, ziehen Sie jsonbstattjson .


6

Stellen Sie sich vor, Sie sind mit einer Gruppe von Freunden unterwegs und das Gespräch dreht sich um Filme. Jemand fragt: "Was denkst du über 'Die drei Musketiere'?" Sie antworten: "Welches?"

Welche zusätzlichen Informationen würden Sie benötigen, um absolut sicher zu sein, dass Sie beide an denselben Film denken? Der Name des Regisseurs? Das Produktionsstudio? Das Jahr, in dem es veröffentlicht wurde? Einer der Namen des Sterns? Eine Kombination von zwei oder mehr?

Die Antwort auf meine und Ihre Frage ist dieselbe.

Ich würde jedoch nicht denken, dass das Genre ein guter Kandidat wäre. Ein Grund, Genre ist ein viel zu subjektives Kriterium. Ist "Die drei Musketiere" Aktion? Theater? Abenteuer? Komödie? Action-Abenteuer? romantische Komödie? Ich sehe oft den gleichen Film unter verschiedenen Genres. Selbst wenn Sie mehrere Genres zulassen, kann Ihr Benutzer ein völlig anderes auswählen, das nicht mit dem tatsächlich gesuchten Film aufgeführt ist.

Sogar die Laufzeiten können unterschiedlich sein, insbesondere zwischen Theater- und VCR / DVD / B-Ray-Versionen.

Sie benötigen also harte, objektive Attribute, die sich nicht von einer Medienmitteilung zur nächsten ändern. Leider kann dies den Namen des Films ausschließen, da bekannt ist, dass Filme umbenannt werden, insbesondere nach der Veröffentlichung einer Fortsetzung.

Was ist mit dem Veröffentlichungsdatum? Der Kinostart von 1993? Die VCR-Veröffentlichung von 1999? Die DVD-Veröffentlichung von 2004? Du hast die Idee.

Was ist mit all den Filmen von Alan Smithee? Hat der echte Regisseur jemals endlich einen Schritt nach vorne gemacht, um das Projekt nachträglich zu benennen? Ich weiß es nicht.

Hmm, ich höre besser auf, solange noch einige Kriterien übrig sind.

Einige zusätzliche Punkte:

  • Ja, behalten Sie den Ersatzschlüssel und erstellen Sie einen eindeutigen Index für die natürlichen Schlüsselfelder (wenn Sie diese endgültig festlegen können). Der Ersatzschlüssel eignet sich am besten für Fremdschlüsselreferenzen. Sie möchten nicht alle natürlichen Schlüsselfelder in jeder Tabelle duplizieren, die einen Verweis auf einen Film enthält.
  • Löschen Sie die Array-Felder (Genres, Tags, Ursprünge). Fahren Sie fort und normalisieren Sie diese Attribute ordnungsgemäß. Ich habe noch nie ein Array-Feld gesehen, das nicht viel mehr Mühe als es wert war, besonders wenn Sie möchten, dass sie durchsuchbar sind ("... wo genre = 'Horror' ..."). Beachten Sie , dass dadurch Probleme mit Groß- und Kleinschreibung und Rechtschreibung ("Science Fiction" vs. "SciFi") nicht automatisch behoben werden - es sei denn, Sie pflegen die Nachschlagetabellen ordnungsgemäß . Es ist jedoch viel einfacher, in einem Feld einer kleinen Tabelle nach solchen Unterschieden zu suchen, als in jeder Array-Zelle jeder Zeile einer großen Tabelle.

4

Die ID-Spalte hat überhaupt keinen Vorteil, wenn es um die Eindeutigkeit geht, die Sie erzwingen möchten / müssen. Die Eindeutigkeit einer beliebigen Kombination von Attributen wird niemals durch Hinzufügen einer bedeutungslosen ID erzwungen. Sein "Vorteil" zeigt sich nur, wenn Sie jemals an den Punkt gelangen, an dem Sie eine neue Tabelle benötigen, für die ein Fremdschlüssel erforderlich ist. In diesem Fall und wenn Sie die ID aufgenommen haben, können Sie diese als FK in Ihrer neuen Tabelle verwenden. (Aber denken Sie nicht, dass es ein kostenloses Mittagessen sein wird. Der Nachteil eines solchen Ansatzes ist, dass Sie wahrscheinlich mehr Joins schreiben, um Informationen abzurufen, die durchaus Teil dieser neuen Tabelle gewesen sein könnten, die Sie erstellt haben. )


1
Wenn die Geschäftsregeln besagen, dass die Kombination von Werten in den Attributen FOO und BAR eindeutig sein muss, wird dies durch Hinzufügen einer ID nicht erreicht. Durch Hinzufügen der ID wird lediglich vermieden, dass FOO und BAR als solche in Referenzierungstabellen aufgenommen werden müssen. Dies erfordert wiederum mehr Verknüpfungen, da die FOO- und BAR-Attribute (die BUSINESS-Kennungen enthalten) nicht dort sind, wo sie hätten sein können (und wo sie sehr wahrscheinlich erwartet werden, zumindest aus geschäftlicher Sicht).
Erwin Smout

1
Es sind NICHT die "Zeilen", die eindeutig sein müssen, sondern das, was das Unternehmen sagt, müssen ihre Kennungen sein. Wenn dies eine Kombination der Attribute FOO und BAR ist, dann ist es die Kombination der Attribute FOO und BAR.
Erwin Smout

2
Die ID zu haben oder nicht, löst kein Problem der Durchsetzung der Eindeutigkeit der "Geschäft" -Spalten in Ihrer Tabelle. Die Durchsetzung der Eindeutigkeit muss durch Deklaration der entsprechenden Schlüssel erfolgen (was Sie tun - die Tatsache, dass Sie das syntaktische Wort "CONSTRAINT" anstelle von "KEY" verwendet haben, bedeutet nicht, dass es kein Schlüssel ist).
Erwin Smout
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.