it-swarm.com.de

Erlaube null in einer eindeutigen Spalte

Ich habe die folgende Tabelle erstellt:

CREATE TABLE MMCompany (
   CompanyUniqueID BIGSERIAL PRIMARY KEY NOT NULL, 
   Name VARCHAR (150) NOT NULL,
   PhoneNumber VARCHAR(20) NOT NULL UNIQUE, 
   Email VARCHAR(75) UNIQUE,
   CompanyLogo BYTEA
 );

Die E-Mail-Spalte ist eindeutig und führt in meinem Szenario zu einem "Fehler", da es nur einen Datensatz mit Null geben kann. Ich versuche, Aufzeichnungen von Unternehmen ohne dieselbe E-Mail-Adresse zu erstellen, aber gleichzeitig zuzulassen, dass Unternehmen keine E-Mail-Adresse haben.

Wie kann ich das erreichen?

32
liv a

Das ist ein Missverständnis.
Die UNIQUE -Beschränkung macht genau was Sie wollen. In einer Spalte, die NULL definiert ist, können mehrere UNIQUE Werte gleichzeitig vorhanden sein.

Zitieren das Handbuch über EINZIGARTIGE Beschränkungen :

Im Allgemeinen wird eine eindeutige Einschränkung verletzt, wenn die Tabelle mehr als eine Zeile enthält, in der die Werte aller in der Einschränkung enthaltenen Spalten gleich sind. Zwei Nullwerte werden in diesem Vergleich jedoch nicht als gleich angesehen. Das bedeutet , dass es auch bei Vorhandensein einer eindeutigen Einschränkung möglich ist, doppelte Zeilen, die einen Nullwert enthalten, in mindestens einer der eingeschränkten Spalten zu speichern. Dieses Verhalten entspricht dem SQL-Standard, es wurde jedoch festgestellt, dass andere SQL-Datenbanken dieser Regel möglicherweise nicht folgen. Seien Sie also vorsichtig, wenn Sie Anwendungen entwickeln, die portabel sein sollen.

Meine kühne Betonung.

Beachten Sie, dass Zeichentypen eine leere Zeichenfolge zulässt (''), das nicht ein NULL -Wert ist und eine eindeutige Verletzung auslösen würde, genau wie jeder andere Nicht-Null-Wert, wenn er in mehr als einer Zeile eingegeben wird.

71

Kein solches Problem in Postgres

In Erwin Brandstetters richtiger Antwort erklärt er, dass Sie tatsächlich das gewünschte Verhalten sehen sollten (mehrere NULL-Werte in einer Unique-Einschränkung zulässig). Sie sollten dieses Verhalten insbesondere in Postgres und in jeder SQL-Standard-kompatiblen Datenbank im Allgemeinen sehen.

Problemumgehung für andere Datenbanken

In der Postgres-Dokumentation wird jedoch auf die Portabilität hingewiesen, da bekannt ist, dass einige Datenbanken gegen diese Funktion verstoßen. Für ein solches nicht konformes System empfehle ich, die Verwendung eines NULL-Wertes in solchen Feldern durch einen Scheinwert zu ersetzen. Der falsche Wert wäre eine Zeichenfolge wie "unknown_" plus ein beliebiger Wert, von dem praktisch sicher ist, dass er eindeutig ist. Dieser willkürliche Wert könnte etwa das aktuelle Datum plus eine Zufallszahl sein.

UUID

Aber anstatt Ihren eigenen willkürlichen Wert zu würfeln, generieren Sie eine [~ # ~] uuid [~ # ~] . Die ursprüngliche UUID der Version 1 ist in der Tat eine Kombination aus dem aktuellen Datum und der Uhrzeit, einer Zufallszahl und der praktisch eindeutigen MAC-Adresse des Computers .

Eine UUID, die als Hex-Zeichenfolge mit kanonischer Formatierung unter Verwendung von Bindestrichen dargestellt wird, sieht folgendermaßen aus:

93e6f268-5c2d-4c63-9d9c-40e6ac034f88

Mein Vorschlag ist also, eine beliebige Zeichenfolge wie "unknown_" plus eine UUID zu kombinieren, um so auszusehen:

unknown_93e6f268-5c2d-4c63-9d9c-40e6ac034f88

Mein Vorschlag für nicht konforme Datenbanken lautet daher, einen solchen Wert zu generieren und anstelle von NULL zu verwenden. Verwenden Sie ihn, wenn in dieser Spalte für eine bestimmte Zeile noch kein bekannter Wert vorhanden ist. Anstatt Abfragen zu schreiben, die nach Zeilen suchen, die einen NULL-Wert in dieser Spalte haben (oder nicht haben), schreiben Sie Abfragen, die nach Zeilen suchen, die einen Wert haben (oder nicht haben), der mit der beliebigen Zeichenfolge "unknown_" beginnt Beispiel. Jede Zeile würde dann die Bedingung erfüllen, einen eindeutigen Wert zu haben.

In der Tat würde ich diesen "unknown_" + UUID-Wert als Standard für diese Spalte zuweisen.

Sie können dieser Spalte auch eine NOT NULL-Einschränkung hinzufügen.

UUID-Werte generieren

Postgres hat eine eingebaute Unterstützung für den Datentyp der UUID, aber das ist in dieser Antwort hier irrelevant. Sie müssen lediglich eine UUID generieren value.

Zum Generieren von UUIDs benötigen Sie eine Erweiterung (Plugin), die diese Funktion zu Postgres hinzufügt. Die meisten Postgres-Installer enthalten eine solche Erweiterung. Diese Erweiterung heißt uuid-ossp . Normalerweise ist die Erweiterung nicht standardmäßig aktiviert. Verwenden Sie dazu in neueren Versionen von Postgres den Befehl CREATE EXTENSION . Anweisungen finden Sie in meinem Blogbeitrag zur Installation in Postgres 9.1 und höher oder meinem anderen Beitrag zu Postgres 9.0 und früher . Sowohl die neue als auch die alte Art der Installation ist einfach, vorausgesetzt, die Erweiterung/das Plugin wurde zusammen mit Ihrer Postgres-Installation kompiliert und gebündelt.

Zusammenfassung

Lassen Sie mich klarstellen, dass für Postgres allein keine Problemumgehung erforderlich ist , da Postgres dem SQL-Standard entspricht. Doch wenn:

  • Sie sind besorgt über die Portabilität Ihres Codes auf ein anderes nicht kompatibles Datenbanksystem oder
  • Sie müssen Daten mit einem nicht kompatiblen Datenbanksystem austauschen oder
  • Sie stimmen zu Dr. Chris Datum , dass NULL das Werk des Teufels ist und vermieden werden sollte

… Dann ist ein Workaround wie dieses notwendig.

17
Basil Bourque

In einigen Datenbanken sind mehrere Nullwerte nicht zulässig. In SQL Server-Dokumentation heißt es beispielsweise, dass "mehrere Nullwerte als Duplikate betrachtet werden". Bei Datenbanken, die keine nullbaren UNIQUE-Einschränkungen zulassen, können Sie Folgendes versuchen (von GuidoGs Antwort zu einer anderen Frage):

CREATE UNIQUE NONCLUSTERED INDEX IDX_Email
ON MMCompany (Email)
WHERE Email IS NOT NULL;
6
dumbledad

Löschen Sie die E-Mail-Spalte aus der Tabelle. Legen Sie es in eine neue Tabelle, in der es NICHT NULL und EINZIGARTIG sein kann:

CREATE TABLE CompanyEmail
 (
    CompanyUniqueID INT NOT NULL PRIMARY KEY
       REFERENCES MMCompany (CompanyUniqueID),
    Email VARCHAR(75) NOT NULL UNIQUE
 );

Vermeiden Sie nullfähige UNIQUE-Einschränkungen.

4
nvogel

Unique und Null vertragen sich nicht viel, da Null per Definition nicht definiert ist - Sie können nicht wissen, ob zwei Nullen gleich sind.

In diesem Sinne ist Ihre derzeit einmalige Einschränkung für E-Mails die richtige Vorgehensweise und sollte so funktionieren, wie sie ist.


Falls Sie es dennoch einmal anders machen müssen, funktioniert ein Teilindex:

create unique index on MMCompany((email is null)) where (email is null);

Ein anderer Ansatz besteht darin, einen Einschränkungstrigger zu definieren. Etwas wie:

create function email_chk() returns trigger as $$
begin
  if exists (
    select 1 from mmcompany where email is null and companyuniqueid <> new.id
  ) then
    raise 'dup null found';
  end if;
  return null;
end;
$$ language plpgsql;

create constraint trigger after insert or update on mmcompany
for each row when (new.email is null)
execute procedure email_chk();
2