Fremdschlüsselbeziehungen erstellen und verwalten

Auf dieser Seite wird beschrieben, wie Sie Fremdschlüsselbeziehungen in Ihrer Datenbank verwalten.

Ein Fremdschlüssel ist eine Spalte, die von mehreren Tabellen gemeinsam verwendet wird, um eine Verknüpfung zwischen zugehörigen Daten herzustellen. Wenn Sie einen Fremdschlüssel verwenden, sorgt Spanner dafür, dass diese Beziehung aufrechterhalten wird.

Das folgende Diagramm zeigt ein einfaches Datenbankschema, in dem Daten in einer Tabelle in Beziehung zu Daten in einer anderen Tabelle stehen.

Beispiel für ein Datenbankschema, das die Beziehung zwischen Tabellen mit Fremdschlüsseln zeigt.

Abbildung 1. Diagramm einer Auftragsverarbeitungsdatenbank

Das in Abbildung 1 gezeigte Schema enthält drei Tabellen:

  • In der Tabelle Customers werden die Namen jedes Kunden aufgezeichnet.
  • Die Tabellen Orders verfolgen alle Bestellungen.
  • In der Tabelle Products werden die Produktinformationen für jedes Produkt gespeichert.

Zwischen diesen Tabellen gibt es zwei Fremdschlüsselbeziehungen:

  • Zwischen der Tabelle Orders und der Tabelle Customers wird eine Fremdschlüsselbeziehung definiert, um sicherzustellen, dass eine Bestellung nur dann angelegt werden kann, wenn eine entsprechender Kunde vorhanden ist.

  • Eine Fremdschlüsselbeziehung zwischen der Tabelle Orders und der Tabelle Products stellt sicher, dass keine Bestellung für ein nicht vorhandenes Produkt erstellt werden kann.

Anhand des vorherigen Schemas werden in diesem Thema die CONSTRAINT-Anweisungen der Datendefinitionssprache (DDL) erläutert, mit denen Sie Beziehungen zwischen Tabellen in einer Datenbank verwalten können.

Standardmäßig sind alle Fremdschlüssel in Spanner erzwungene Fremdschlüssel, die die referenzielle Integrität erzwingen. In Spanner können Sie auch Informationsfremdschlüssel verwenden, mit denen die referenzielle Integrität nicht validiert oder erzwungen wird. Weitere Informationen finden Sie unter Vergleich von Fremdschlüsseln und Auswählen des zu verwendenden Fremdschlüsseltyps. Wenn nicht anders angegeben, sind die Fremdschlüssel in den Beispielen auf dieser Seite erzwungene Fremdschlüssel.

Einer neuen Tabelle einen Fremdschlüssel hinzufügen

Angenommen, Sie haben in Ihrer einfachen Produktbestellungsdatenbank eine Tabelle Customers erstellt. Sie benötigen nun eine Orders-Tabelle, um Informationen zu den Bestellungen von Kunden zu speichern. Damit alle Bestellungen gültig sind, darf das System keine Zeilen in die Tabelle Orders einfügen, wenn es keinen übereinstimmenden Eintrag in der Tabelle Customers gibt. Daher benötigen Sie einen erzwungenen Fremdschlüssel, um eine Beziehung zwischen den beiden Tabellen herzustellen. Eine Möglichkeit besteht darin, der neuen Tabelle eine CustomerID-Spalte hinzuzufügen und sie als Fremdschlüssel zu verwenden, um eine Beziehung zur CustomerID-Spalte in der Tabelle Customers zu erstellen.

Wenn Sie eine neue Tabelle mit einem Fremdschlüssel erstellen, verwenden Sie REFERENCE, um eine Beziehung zu einer anderen Tabelle herzustellen. Die Tabelle, die die REFERENCE-Anweisung enthält, wird als Referenztabelle bezeichnet. Die in der REFERENCE-Anweisung angegebene Tabelle ist die Tabelle, auf die verwiesen wird. Die Spalte, die in der REFERENCE-Anweisung angegeben ist, wird als Referenzspalte bezeichnet.

Im folgenden Beispiel wird gezeigt, wie Sie die DDL-Anweisung CREATE TABLE verwenden, um die Tabelle Orders mit einer Fremdschlüsseleinschränkung zu erstellen, die in der Tabelle Customers auf CustomerID verweist.

GoogleSQL

CREATE TABLE Orders (
  OrderID INT64 NOT NULL,
  CustomerID INT64 NOT NULL,
  Quantity INT64 NOT NULL,
  ProductID INT64 NOT NULL,
  CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID)
) PRIMARY KEY (OrderID);

PostgreSQL

CREATE TABLE Orders (
  OrderID BIGINT NOT NULL,
  CustomerID BIGINT NOT NULL,
  Quantity BIGINT NOT NULL,
  ProductID BIGINT NOT NULL,
  CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID),
  PRIMARY KEY (OrderID)
);

Die obige Anweisung enthält eine CONSTRAINT-Klausel mit den folgenden Merkmalen:

  • Mit der CONSTRAINT-Syntax können Sie eine Einschränkung benennen und so leichter den ausgewählten Namen verwenden.

  • Die Einschränkung hat den Namen FK_CustomerOrder. Einschränkungsnamen gelten für das Schema und müssen innerhalb des Schemas eindeutig sein.

  • Die Tabelle Orders, auf der die Einschränkung definiert wird, ist die Referenztabelle. Die Tabelle Customers ist die referenzierte Tabelle.

  • Die Referenzspalte in der Referenztabelle ist CustomerID. Sie verweist auf das Feld CustomerID in der Tabelle Customers. Wenn jemand versucht, eine Zeile in Orders mit einem CustomerID einzufügen, der in Customers nicht vorhanden ist, schlägt das Einfügen fehl.

Das folgende Beispiel zeigt eine alternative Tabellenerstellungsanweisung. Hier wird die Fremdschlüsseleinschränkung ohne Namen definiert. Wenn Sie diese Syntax verwenden, generiert Spanner einen Namen für Sie. Informationen zum Ermitteln der Namen aller Fremdschlüssel finden Sie unter Eigenschaften einer Fremdschlüsselbeziehung ansehen.

GoogleSQL

CREATE TABLE Orders (
  OrderID INT64 NOT NULL,
  CustomerID INT64 NOT NULL,
  ProductID INT64 NOT NULL,
  FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID)
) PRIMARY KEY (OrderID);

PostgreSQL

CREATE TABLE Orders (
  OrderID BIGINT NOT NULL,
  CustomerID BIGINT NOT NULL,
  Quantity BIGINT NOT NULL,
  ProductID BIGINT NOT NULL,
  FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID),
  PRIMARY KEY (OrderID)
);

Fremdschlüssel einer vorhandenen Tabelle hinzufügen

Außerdem sollen Kunden nur vorhandene Produkte bestellen können. Wenn Ihre Tabelle vorhandene Einschränkungen hat, müssen Sie alle Einschränkungen entfernen. In Spanner müssen alle erzwungenen Einschränkungen in einer Tabelle gleichzeitig in einer einzelnen Batch-DDL-Anweisung implementiert werden.

Wenn Ihre Tabelle keine vorhandenen Einschränkungen hat, können Sie die DDL-Anweisung ALTER TABLE verwenden, um der vorhandenen Tabelle Orders eine erzwungene Fremdschlüsseleinschränkung hinzuzufügen, wie im folgenden Beispiel gezeigt:

ALTER TABLE Orders
  ADD CONSTRAINT DB_ProductOrder FOREIGN KEY (ProductID) REFERENCES Products (ProductID);

Die Referenzspalte in Orders ist ProductID und verweist auf die Spalte ProductID in Products. Wenn Sie Cloud Spanner mit einem dieser Namen versehen haben, können Sie noch einmal die folgende Syntax verwenden:

ALTER TABLE Orders
  ADD FOREIGN KEY (ProductID) REFERENCES Products (ProductID);

Einer neuen Tabelle einen Fremdschlüssel mit einer Löschaktion hinzufügen

Erinnern Sie sich an das vorherige Beispiel, in dem Sie eine Customers-Tabelle in einer Produktbestellungsdatenbank haben, die eine Orders-Tabelle benötigt. Sie möchten eine Fremdschlüsseleinschränkung hinzufügen, die auf die Tabelle Customers verweist. Sie möchten jedoch sicherstellen, dass Spanner in Zukunft auch alle Bestellungen für diesen Kunden löscht, wenn Sie einen Kundendatensatz löschen. In diesem Fall verwenden Sie die Aktion ON DELETE CASCADE mit der Fremdschlüsseleinschränkung.

Die folgende CREATE TABLE-DDL-Anweisung für die Orders-Tabelle enthält die Fremdschlüsseleinschränkung, die mit einer ON DELETE CASCADE-Aktion auf die Customers-Tabelle verweist.

GoogleSQL

CREATE TABLE Orders (
  OrderID INT64 NOT NULL,
  CustomerID INT64 NOT NULL,
  Quantity INT64 NOT NULL,
  ProductID INT64 NOT NULL,
  CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerID)
    REFERENCES Customers (CustomerID) ON DELETE CASCADE
) PRIMARY KEY (OrderID);

PostgreSQL

CREATE TABLE Orders (
  OrderID BIGINT NOT NULL,
  CustomerID BIGINT NOT NULL,
  Quantity BIGINT NOT NULL,
  ProductID BIGINT NOT NULL,
  FOREIGN KEY (CustomerID)
    REFERENCES Customers (CustomerID) ON DELETE CASCADE,
  PRIMARY KEY (OrderID)
);

Die vorherige Anweisung enthält eine Fremdschlüsseleinschränkung mit einer ON DELETE CASCADE-Klausel. Die Spalte CustomerID ist ein Fremdschlüssel, der auf das Feld CustomerID in der Tabelle Customers verweist. Das bedeutet, dass jeder CustomerID-Wert in der Tabelle Orders auch in der Tabelle Customers vorhanden sein muss. Wenn jemand versucht, eine Zeile aus der Tabelle Customers zu löschen, werden alle Zeilen in der Tabelle Orders, die auf den gelöschten CustomerID-Wert verweisen, in derselben Transaktion ebenfalls gelöscht.

Einer Tabelle einen Fremdschlüssel mit einer Löschaktion hinzufügen

Außerdem sollen Aufträge nur für vorhandene Produkte erstellt werden können. Sie können ALTER TABLE verwenden, um der Auftragstabelle eine weitere Fremdschlüsseleinschränkung mit der Aktion ON DELETE CASCADE so hinzuzufügen:

ALTER TABLE Orders
  ADD CONSTRAINT DB_ProductOrder FOREIGN KEY (ProductID)
    REFERENCES Products (ProductID) ON DELETE CASCADE;

Wenn Sie eine Zeile aus der Tabelle Products löschen, werden alle Zeilen in der Tabelle Orders gelöscht, die auf den gelöschten Wert ProductID verweisen.

Informative Fremdschlüssel verwenden (nur GoogleSQL)

Mit Informationsfremdschlüsseln kann der Abfrageoptimierer die Fremdschlüsselbeziehung nutzen, ohne dass der Overhead entsteht, der durch die von erzwingbaren Fremdschlüsseln ausgeführten Prüfungen der referenziellen Integrität entsteht. Informative Fremdschlüssel sind nützlich, wenn die Durchsetzung einer strengen referenziellen Integrität entweder unpraktisch ist oder einen erheblichen Leistungsaufwand verursacht.

Angenommen, Sie möchten die Beziehungen zwischen den Tabellen Customers, Orders und Products modellieren. Die Durchsetzung einer strengen referenziellen Integrität in den Daten der Tabellen kann jedoch zu Leistungsengpässen führen, insbesondere während der Haupteinkaufszeiten mit hohem Bestellvolumen. Außerdem können Kunden Bestellungen für Produkte aufgeben, die eingestellt und aus der Tabelle Products entfernt wurden.

Sie können die Tabelle Orders mit informativen Fremdschlüsseln erstellen:

CREATE TABLE Orders (
    OrderID INT64 NOT NULL,
    CustomerID INT64 NOT NULL,
    Quantity INT64 NOT NULL,
    ProductID INT64 NOT NULL,
    CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID) NOT ENFORCED,
    CONSTRAINT FK_ProductOrder FOREIGN KEY (ProductID) REFERENCES Products (ProductID) NOT ENFORCED
) PRIMARY KEY (OrderID);

Wenn Sie einen informativen Fremdschlüssel mit NOT ENFORCED erstellen, kann es vorkommen, dass eine Bestellung auf einen nicht vorhandenen Kunden oder ein nicht vorhandenes Produkt verweist. Die Verwendung eines informativen Fremdschlüssels anstelle einer erzwungenen Fremdschlüsselbeschränkung ist eine gute Wahl, wenn ein Kundenkonto gelöscht oder ein Produkt eingestellt werden könnte. Bei einem informativen Fremdschlüssel führt Spanner keine Validierung der referenziellen Integrität durch. Dadurch wird der Schreibaufwand reduziert, was die Leistung während der Spitzenzeiten der Auftragsbearbeitung potenziell verbessert.

Sie können dem Abfrageoptimierer erlauben, die Beziehungen zu verwenden, um effiziente Abfragepläne zu erstellen. Dadurch kann die Leistung von Abfragen verbessert werden, bei denen die Tabellen über Fremdschlüsselspalten verknüpft werden. Weitere Informationen finden Sie unter Informativer Fremdschlüssel zur Abfrageoptimierung.

Daten über Fremdschlüsselbeziehungen abfragen

SELECT * FROM Orders
  INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID
  INNER JOIN Products ON Orders.ProductsID = Products.ProductID;

Referenzielle Integrität mit erzwungenen Fremdschlüsseln

Der wichtigste Grund für das Hinzufügen von erzwungenen Fremdschlüsselbeziehungen ist, dass Spanner die referenzielle Integrität Ihrer Daten aufrechterhalten kann. Wenn Sie Daten so ändern, dass eine Einschränkung für einen Fremdschlüssel aufgehoben wird, schlägt die Aktualisierung mit einem Fehler fehl.

Sehen Sie sich die Daten in Abbildung 2 an. Einige Kunden haben Produkte bestellt, wie in der Tabelle Orders angegeben. Aufgrund der erzwingbaren Fremdschlüsseleinschränkung haben die in die Tabelle Orders eingefügten Daten eine referenzielle Integrität.

Beispieldaten für die Tabellen „Kunden“, „Produkte“ und „Bestellungen“

Abbildung 2. Beispieldaten in unserer Bestelldatenbank.

Die folgenden Beispiele zeigen, was passiert, wenn Sie versuchen, die Daten so zu ändern, dass die referenzielle Integrität beeinträchtigt wird.

  • Fügen Sie der Tabelle Orders eine Zeile mit einem CustomerID-Wert hinzu, der in Customers nicht vorhanden ist.

    Was passiert, wenn Sie anhand der Beispieldaten aus dem vorherigen Diagramm die folgende Änderung vorzunehmen versuchen?

    INSERT INTO Orders (OrderID, ProductID, Quantity, CustomerID)
      VALUES (19, 337876, 4, 447);
    

    In diesem Fall versucht das System, eine Zeile in Orders mit einer CustomerID (447) einzufügen, die in der Tabelle Customers nicht vorhanden ist. Wenn das System dies tun würde, hätten Sie eine ungültige Bestellung in Ihrem System. Mit der erzwungenen Fremdschlüsseleinschränkung, die Sie der Tabelle Orders hinzugefügt haben, ist Ihre Tabelle jedoch geschützt. Das INSERT schlägt mit der folgenden Meldung fehl, vorausgesetzt, die Beschränkung heißt FK_CustomerOrder.

    Foreign key constraint `FK_CustomerOrder` is violated on table `Orders`.
    Cannot find referenced values in Customers(CustomerID).
    

    Im Gegensatz zu erzwungenen Fremdschlüsseln wird bei informativen Fremdschlüsseln die referenzielle Integrität nicht erzwungen. Wenn FK_CustomerOrder ein informativer Fremdschlüssel ist, wird die Einfügeanweisung erfolgreich ausgeführt, da Spanner nicht prüft, ob der entsprechende CustomerID in der Tabelle Customers vorhanden ist. Aus diesem Grund entsprechen die Daten möglicherweise nicht der referenziellen Integrität, die durch FK_CustomerOrder definiert ist.

  • Es wird versucht, eine Zeile aus der Tabelle Customers zu löschen, wenn auf den Kunden in einer erzwingbaren Fremdschlüsseleinschränkung verwiesen wird.

    Stellen wir uns eine Situation vor, in der ein Kunde Ihren Onlineshop abonniert. Sie möchten den Kunden aus Ihrem Back-End entfernen und führen den folgenden Vorgang aus.

    DELETE FROM Customers WHERE CustomerID = 721;
    

    In diesem Beispiel erkennt Spanner über die Einschränkung für Fremdschlüssel, dass in der Tabelle Orders weiterhin Datensätze vorhanden sind, die auf die zu löschende Kundenzeile verweisen. In diesem Fall wird der folgende Fehler angezeigt.

    Foreign key constraint violation when deleting or updating referenced row(s): referencing row(s) found in table `Orders`.

    Zur Behebung dieses Problems löschen Sie zuerst alle Verweise auf Orders. Sie können den Fremdschlüssel auch mit der Aktion ON DELETE CASCADE definieren, damit Spanner das Löschen von verweisenden Einträgen übernimmt.

    Wenn FK_CustomerOrder ein informativer Fremdschlüssel ist, ist die Löschaktion ebenfalls erfolgreich, da Spanner die referenzielle Integrität von informativen Fremdschlüsseln nicht garantiert.

Eigenschaften einer Fremdschlüsselbeziehung anzeigen

Das INFORMATION_SCHEMA von Cloud Spanner enthält Informationen zu Fremdschlüsseln und ihren Sicherungsindizes. Im Folgenden finden Sie einige Beispiele für Fragen, die Sie durch Abfragen des INFORMATION SCHEMA beantworten können.

Weitere Informationen zum Sichern von Indizes finden Sie unter Sicherungsindex für Fremdschlüssel.

Welche Einschränkungen sind in meiner Datenbank definiert?

SELECT tc.CONSTRAINT_NAME, tc.TABLE_NAME, tc.CONSTRAINT_TYPE
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
WHERE tc.CONSTRAINT_TYPE = 'FOREIGN KEY';

Welche Fremdschlüssel sind in meiner Datenbank definiert?

SELECT rc.CONSTRAINT_NAME, rc.UNIQUE_CONSTRAINT_NAME, rc.SPANNER_STATE
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as rc;

Welche Indexe sind sekundäre Indexe für Fremdschlüssel, die auch als Sicherungsindexe bezeichnet werden?

Fremdschlüssel-Sicherungsindexe werden von Spanner verwaltet , sodass die Abfrage für SPANNER_IS_MANAGED in der INDEXES-Ansicht alle Sicherungsindexe zurückgibt.

SELECT i.TABLE_NAME, i.INDEX_NAME, i.INDEX_TYPE, i.INDEX_STATE,
  i.IS_UNIQUE, i.IS_NULL_FILTERED, i.SPANNER_IS_MANAGED
FROM INFORMATION_SCHEMA.INDEXES as i
WHERE SPANNER_IS_MANAGED = 'YES';

Welche referenzielle Aktion ist mit der Fremdschlüsseleinschränkung definiert?

SELECT rc.CONSTRAINT_NAME, rc.UNIQUE_CONSTRAINT_NAME, rc.DELETE_RULE,
  rc.UPDATE_RULE
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as rc;

Wird ein Fremdschlüssel erzwungen oder nicht erzwungen?

SELECT tc.CONSTRAINT_NAME, tc.TABLE_NAME, tc.CONSTRAINT_TYPE, tc.ENFORCED
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
WHERE tc.CONSTRAINT_TYPE = 'FOREIGN KEY';

Weitere Informationen finden Sie im Informationsschema.

Fremdschlüsselbeziehung entfernen

Die folgende DDL löscht eine Fremdschlüsseleinschränkung aus der Tabelle Orders.

ALTER TABLE Orders
  DROP CONSTRAINT FK_CustomerOrder;

Die Fremdschlüssel-Sicherungsindexe werden automatisch entfernt, wenn die Beschränkung selbst verworfen wird.

Unterstützung von komplexeren Fremdschlüsselbeziehungen

In den folgenden Themen wird beschrieben, wie Sie mit Fremdschlüsseln komplexere Beziehungen zwischen Tabellen erzwingen.

Mehrere Spalten

Fremdschlüssel können auf mehrere Spalten verweisen. Die Liste der Spalten bildet einen Schlüssel, der dem Primärschlüssel einer Tabelle oder einem Sicherungsindex entspricht. Die Referenztabelle enthält Fremdschlüssel des referenzierten Tabellenschlüssels.

Die erzwungenen Fremdschlüsseldefinitionen im folgenden Beispiel geben Folgendes an:

  • Jeder SongName-Wert in der TopHits-Tabelle muss einen entsprechenden Wert in der Songs-Tabelle haben.

  • Für jedes SingerFirstName- und SingerLastName-Wertepaar muss ein entsprechendes FirstName- und LastName-Wertepaar in der Tabelle Singers vorhanden sein.

GoogleSQL

CREATE TABLE TopHits (
  Rank INT64 NOT NULL,
  SongName STRING(MAX),
  SingerFirstName STRING(MAX),
  SingerLastName STRING(MAX),

  -- Song names must either be NULL or have matching values in Songs.
  FOREIGN KEY (SongName) REFERENCES Songs (SongName),

  -- Singer names must either be NULL or have matching values in Singers.
  FOREIGN KEY (SingerFirstName, SingerLastName)
  REFERENCES Singers (FirstName, LastName)

) PRIMARY KEY (Rank);

PostgreSQL

CREATE TABLE TopHits (
  Rank BIGINT NOT NULL,
  SongName VARCHAR,
  SingerFirstName VARCHAR,
  SingerLastName VARCHAR,

  -- Song names must either be NULL or have matching values in Songs.
  FOREIGN KEY (SongName) REFERENCES Songs (SongName),

  -- Singer names must either be NULL or have matching values in Singers.
  FOREIGN KEY (SingerFirstName, SingerLastName)
  REFERENCES Singers (FirstName, LastName),

  PRIMARY KEY (Rank)
);

Zirkelverweise

Gelegentlich haben Tabellen Zirkelverweise, möglicherweise aus Legacy-Gründen oder aufgrund von Denormalisierung. Spanner-Fremdschlüssel lassen Zirkelverweise zu. Da eine referenzierte Tabelle vorhanden sein muss, bevor ein Fremdschlüssel darauf verweisen kann, muss einer der Fremdschlüssel mit einer ALTER TABLE-Anweisung hinzugefügt werden. Beispiel:

  1. Erstellen Sie TableA ohne Fremdschlüssel.
  2. Erstellen Sie TableB mit einer Fremdschlüsseleinschränkung für TableA.
  3. Verwenden Sie ALTER TABLE in TableA, um einen Fremdschlüsselverweis auf TableB zu erstellen.

Auf sich selbst verweisende Tabellen

Ein spezieller Typ von Zirkelverweis ist eine Tabelle, die einen Fremdschlüssel definiert, der auf dieselbe Tabelle verweist. Das folgende Snippet zeigt beispielsweise einen Fremdschlüssel, der erzwingt, dass die Manager-ID eines Mitarbeiters auch ein Mitarbeiter ist.

GoogleSQL

CREATE TABLE Employees (
  EmployeeId INT64 NOT NULL,
  EmployeeName STRING(MAX) NOT NULL,
  ManagerId INT64,
  FOREIGN KEY (ManagerId) REFERENCES Employees (EmployeeId)
) PRIMARY KEY (EmployeeId);

PostgreSQL

CREATE TABLE Employees (
  EmployeeId BIGINT NOT NULL,
  EmployeeName VARCHAR NOT NULL,
  ManagerId BIGINT,
  FOREIGN KEY (ManagerId) REFERENCES Employees (EmployeeId),
  PRIMARY KEY (EmployeeId)
);

Nächste Schritte