יצירה וניהול של קשרי מפתח זר

בדף הזה מוסבר איך לנהל קשרי גומלין של מפתחות זרים במסד הנתונים.

מפתח זר הוא עמודה שמשותפת בין טבלאות כדי ליצור קישור בין נתונים קשורים. כשמשתמשים במפתח זר, Spanner מוודא שהקשר הזה נשמר.

התרשים הבא מציג סכימה בסיסית של מסד נתונים, שבה לנתונים בטבלה אחת יש קשר לנתונים בטבלה אחרת.

דוגמה לסכימה של מסד נתונים שמציגה קשרים של מפתחות זרים בין טבלאות.

איור 1. תרשים של סכימת מסד נתונים לעיבוד הזמנות

בסכימה שמוצגת באיור 1 יש שלוש טבלאות:

  • בטבלה Customers מתועדים השמות של כל הלקוחות.
  • בטבלה Orders מתבצע מעקב אחרי כל ההזמנות שבוצעו.
  • בטבלה Products מאוחסנים פרטי המוצרים של כל מוצר.

יש שני קשרים של מפתח זר בין הטבלאות האלה:

  • קשר של מפתח זר מוגדר בין הטבלה Orders לטבלה Customers כדי לוודא שאי אפשר ליצור הזמנה בלי לקוח תואם.

  • קשר של מפתח זר בין הטבלה Orders לטבלה Products מבטיח שלא תיווצר הזמנה למוצר שלא קיים.

במאמר הזה נשתמש בסכימה הקודמת כדוגמה, ונסביר על הצהרות של שפת הגדרת נתונים (DDL) ב-CONSTRAINT שאפשר להשתמש בהן כדי לנהל קשרים בין טבלאות במסד נתונים.

כברירת מחדל, כל המפתחות הזרים ב-Spanner הם מפתחות זרים שנאכפים, שאוכפים שלמות של הפניות. ב-Spanner אפשר גם להשתמש במפתחות זרים אינפורמטיביים, שלא מאמתים או אוכפים את שלמות ההפניות. מידע נוסף זמין במאמרים השוואה בין מפתחות זרים ובחירת סוג המפתח הזר שבו רוצים להשתמש. אם לא מציינים את סוג המפתח הזר, המפתחות הזרים בדוגמאות שבדף הזה הם מפתחות זרים שנאכפים.

הוספת מפתח זר לטבלה חדשה

נניח שיצרתם טבלה בשם Customers במסד הנתונים הבסיסי של הזמנת המוצרים. עכשיו צריך טבלה בשם Orders כדי לאחסן מידע על ההזמנות שהלקוחות מבצעים. כדי לוודא שכל ההזמנות תקינות, אתם לא רוצים לאפשר למערכת להוסיף שורות לטבלה Orders אלא אם יש גם רשומה תואמת בטבלה Customers. לכן, צריך מפתח זר כדי ליצור קשר בין שתי הטבלאות. אפשרות אחת היא להוסיף עמודה CustomerID לטבלה החדשה ולהשתמש בה כמפתח הזר כדי ליצור קשר עם העמודה CustomerID בטבלה Customers.

כשיוצרים טבלה חדשה עם מפתח זר, משתמשים ב-REFERENCE כדי ליצור קשר לטבלה אחרת. הטבלה שמכילה את ההצהרה REFERENCE נקראת הטבלה המפנה. הטבלה שמופיעה בהצהרה REFERENCE היא הטבלה שאליה מפנים. העמודה שמופיעה בהצהרה REFERENCE נקראת העמודה המפנה.

בדוגמה הבאה אפשר לראות איך משתמשים בהצהרת ה-DDL‏ CREATE TABLE כדי ליצור את הטבלה Orders עם אילוץ מפתח זר שמפנה אל CustomerID בטבלה Customers.

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)
);

ההצהרה הקודמת מכילה סעיף CONSTRAINT עם המאפיינים הבאים:

  • שימוש בתחביר CONSTRAINT כדי לתת שם לאילוץ, כך שיהיה קל יותר למחוק את הטבלה באמצעות השם שבחרתם.

  • שם האילוץ הוא FK_CustomerOrder. שמות האילוצים מוגדרים ברמת הסכימה, והם חייבים להיות ייחודיים בתוך הסכימה.

  • הטבלה Orders, שבה מגדירים את האילוץ, היא הטבלה המפנה. הטבלה Customers היא הטבלה שאליה מפנים.

  • העמודה המפנה בטבלה המפנה היא CustomerID. הוא מפנה לשדה CustomerID בטבלה Customers. אם מישהו מנסה להוסיף שורה ל-Orders עם CustomerID שלא קיים ב-Customers, ההוספה נכשלת.

בדוגמה הבאה מוצגת הצהרה חלופית ליצירת טבלה. כאן, האילוץ של המפתח הזר מוגדר בלי שם. כשמשתמשים בתחביר הזה, Spanner יוצר שם בשבילכם. כדי לגלות את השמות של כל המפתחות הזרים, עיינו במאמר הצגת מאפיינים של קשר מפתח זר.

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)
);

הוספת מפתח זר לטבלה קיימת

אתם גם רוצים לוודא שהלקוחות יוכלו להזמין רק מוצרים שקיימים. אם בטבלה יש אילוצים קיימים, צריך להסיר את כל האילוצים. ב-Spanner, צריך להטמיע את כל האילוצים שנאכפים בטבלה בו-זמנית, בהצהרת DDL אחת באצווה.

אם בטבלה אין אילוצים קיימים, אתם יכולים להשתמש בהצהרת ה-DDL של ALTER TABLE כדי להוסיף אילוץ מפתח זר שנאכף לטבלה Orders הקיימת, כמו בדוגמה הבאה:

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

העמודה המפנה ב-Orders היא ProductID, והיא מפנה לעמודה ProductID ב-Products. אם אתם רוצים ש-Spanner ייתן שמות לאילוצים האלה, השתמשו בתחביר הבא:

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

הוספת מפתח זר עם פעולת מחיקה לטבלה חדשה

נזכיר את הדוגמה הקודמת שבה יש טבלה Customers במסד נתונים של הזמנת מוצרים שצריך טבלה Orders. אתם רוצים להוסיף אילוץ של מפתח זר שמפנה לטבלה Customers. עם זאת, אתם רוצים לוודא שכשאתם מוחקים רשומת לקוח בעתיד, Spanner גם מוחק את כל ההזמנות של אותו לקוח. במקרה כזה, כדאי להשתמש בפעולה ON DELETE CASCADE עם אילוץ המפתח הזר.

הצהרת ה-DDL הבאה של CREATE TABLE לטבלה Orders כוללת את אילוץ המפתח הזר שמפנה לטבלה Customers עם פעולת ON DELETE CASCADE.

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)
);

ההצהרה הקודמת מכילה אילוץ של מפתח זר עם סעיף ON DELETE CASCADE. העמודה CustomerID היא מפתח זר שמפנה לשדה CustomerID בטבלה Customers. כלומר, כל ערך של CustomerID בטבלה Orders חייב להיות קיים גם בטבלה Customers. אם מישהו מנסה למחוק שורה מהטבלה Customers, כל השורות בטבלה Orders שמפנות לערך CustomerID שנמחק נמחקות גם הן באותה עסקה.

באופן דומה, אפשר להשתמש בפעולה ON DELETE SET NULL כדי להגדיר את העמודות המפנות ל-NULL במקום למחוק את השורות המפנות. כדי להשתמש ב-ON DELETE SET NULL, העמודות המפנות צריכות להיות ניתנות לאיפוס (כלומר, לא מסומנות כ-NOT NULL). הן לא יכולות להיות חלק מהמפתח הראשי של הטבלה המפנה או מהעמודות שנוצרו.

במאמר שימוש במשאבים לפעולות מחיקה של מפתחות זרים מוסבר איך ON DELETE SET NULL ו-ON DELETE CASCADE משתווים ב מוטציות, בקריאות ובהתנהגות מדורגת.

הצהרת ה-DDL הבאה של CREATE TABLE לטבלה Orders כוללת אילוץ של מפתח זר שמפנה לטבלה Customers עם פעולת ON DELETE SET NULL. בדוגמה הזו, CustomerID בטבלה Orders יכול להיות null.

GoogleSQL

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

PostgreSQL

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

הוספת מפתח זר עם פעולת מחיקה לטבלה

אתם גם רוצים לוודא שהזמנות נוצרות רק למוצרים שקיימים. אפשר להשתמש ב-ALTER TABLE כדי להוסיף עוד אילוץ של מפתח זר עם פעולת ON DELETE CASCADE לטבלת ההזמנות, באופן הבא:

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

מחיקת שורה מהטבלה Products מוחקת את כל השורות בטבלה Orders שמפנות לערך ProductID שנמחק. אפשר גם להשתמש ב-ON DELETE SET NULL בהצהרת ALTER TABLE אם העמודה שמפנים אליה יכולה להיות null, היא לא חלק מהמפתח הראשי והיא לא עמודה שנוצרה.

שימוש במפתחות זרים אינפורמטיביים (GoogleSQL בלבד)

מפתחות זרים אינפורמטיביים מאפשרים לכלי לאופטימיזציה של שאילתות להשתמש בקשר של המפתח הזר, בלי התקורה שנובעת מבדיקות של שלמות ההפניה שמתבצעות על ידי מפתחות זרים שנאכפים. מפתחות זרים לאינפורמציה שימושיים כשאכיפה של שלמות קשרים קפדנית היא לא מעשית או כשהיא גורמת לתקורה משמעותית בביצועים.

נמשיך עם הדוגמה הקודמת, ונניח שאתם רוצים ליצור מודל של הקשרים בין הטבלאות Customers,‏ Orders ו-Products. עם זאת, אכיפה של שלמות קשרים מחמירה בנתונים של הטבלאות עלולה לגרום לצווארי בקבוק בביצועים, במיוחד בתקופות שיא של קניות עם נפחי הזמנות גבוהים. בנוסף, לקוחות עשויים להזמין מוצרים שהייצור שלהם הופסק והם הוסרו מהטבלה Products.

אתם יכולים ליצור את הטבלה Orders באמצעות מפתחות זרים אינפורמטיביים:

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);

אם יוצרים מפתח זר אינפורמטיבי באמצעות NOT ENFORCED, יכול להיות שהזמנה תפנה ללקוח או למוצר שלא קיימים. שימוש במפתח זר אינפורמטיבי במקום אילוץ מפתח זר נאכף הוא בחירה טובה אם חשבון לקוח עשוי להימחק או אם ייצור של מוצר עשוי להיפסק. כשמשתמשים במפתח זר אינפורמטיבי, Spanner לא מבצע אימות של תקינות ההפניות. כך מצמצמים את התקורה של פעולות הכתיבה, ויכול להיות שהביצועים ישתפרו בזמני שיא של עיבוד הזמנות.

אתם יכולים לאפשר לאופטימיזציית השאילתות להשתמש בקשרים כדי ליצור תוכניות לביצוע שאילתה יעילות. כך אפשר לשפר את הביצועים של שאילתות שמחברות את הטבלאות בעמודות של מפתחות זרים. מידע נוסף זמין במאמר מפתח זר אינפורמטיבי לאופטימיזציה של שאילתות.

שליחת שאילתות על נתונים בקשרים של מפתחות זרים

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

תקינות הפניות עם מפתחות זרים שנאכפים

הסיבה העיקרית להוספת קשרי מפתח זר באכיפה היא כדי ש-Spanner יוכל לשמור על שלמות ההפניות של הנתונים. אם משנים נתונים באופן שמפר אילוץ של מפתח זר, העדכון נכשל ומוצגת שגיאה.

נבחן את הנתונים באיור 2. חלק מהלקוחות הזמינו מוצרים, כפי שמוצג בטבלה Orders. בגלל אילוץ המפתח הזר שנאכף, הנתונים שהוכנסו לטבלה Orders הם בעלי שלמות רפרנציאלית.

נתונים לדוגמה לטבלאות `Customers`, ‏ `Products` ו-`Orders`.

איור 2. נתונים לדוגמה במסד הנתונים של ההזמנות שלנו.

הדוגמאות הבאות מראות מה קורה כשמנסים לשנות את הנתונים באופן שיפגע בשלמות הרפרנציאלית.

  • מוסיפים שורה לטבלה Orders עם ערך CustomerID שלא קיים ב-Customers

    מה קורה אם מנסים לבצע את השינוי הבא, בהתאם לנתונים לדוגמה מהתרשים הקודם?

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

    במקרה כזה, המערכת תנסה להוסיף שורה ל-Orders עם CustomerID (447) שלא קיים בטבלה Customers. אם המערכת הייתה עושה את זה, הייתה לכם הזמנה לא חוקית במערכת. עם זאת, הטבלה Orders מוגנת בזכות אילוץ המפתח הזר שהוספתם לה. הפעולה INSERT נכשלת ומוצגת ההודעה הבאה, בהנחה שהאילוץ נקרא FK_CustomerOrder.

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

    בניגוד למפתחות זרים שנאכפים, מפתחות זרים אינפורמטיביים לא אוכפים שלמות של הפניות. אם FK_CustomerOrder הוא מפתח זר אינפורמטיבי, הצהרת ההוספה תצליח כי Spanner לא מאמת ש-CustomerID התואם קיים בטבלה Customers. לכן, יכול להיות שהנתונים לא יהיו תואמים לשלמות הייחוס שהוגדרה על ידי FK_CustomerOrder.

  • ניסיון למחוק שורה מהטבלה Customers כשהלקוח מאוזכר באילוץ מפתח זר שנאכף.

    נניח שלקוח מבטל את המינוי לחנות האונליין שלכם. אתם רוצים להסיר את הלקוח מה-backend, ולכן אתם מנסים לבצע את הפעולה הבאה.

    DELETE FROM Customers WHERE CustomerID = 721;
    

    בדוגמה הזו, Spanner מזהה באמצעות אילוץ המפתח הזר שעדיין יש רשומות בטבלה Orders שמפנות לשורת הלקוח שאתם מנסים למחוק. במקרה כזה, תוצג השגיאה הבאה.

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

    כדי לפתור את הבעיה, קודם צריך למחוק את כל הרשומות שמפנות אל Orders. אפשר גם להגדיר את המפתח הזר באמצעות הפעולה ON DELETE CASCADE כדי לאפשר ל-Spanner לטפל במחיקה של רשומות מפנות.

    באופן דומה, אם FK_CustomerOrder הוא מפתח זר אינפורמטיבי, פעולת המחיקה תצליח כי Spanner לא מבטיח את השלמות הרפרנציאלית של מפתחות זרים אינפורמטיביים.

הצגת המאפיינים של קשר מפתח זר

‫INFORMATION_SCHEMA של Spanner מכיל מידע על מפתחות זרים ועל האינדקסים לגיבוי שלהם. הנה כמה דוגמאות לשאלות שאפשר לענות עליהן באמצעות שאילתה ב-INFORMATION SCHEMA.

מידע נוסף על אינדקסים לגיבוי זמין במאמר אינדקסים לגיבוי של מפתחות זרים.

אילו אילוצים מוגדרים במסד הנתונים שלי?

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

אילו מפתחות זרים מוגדרים במסד הנתונים שלי?

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

אילו אינדקסים הם אינדקסים משניים למפתחות זרים, שנקראים גם אינדקסים לגיבוי?

האינדקסים לגיבוי של מפתחות זרים מנוהלים על ידי Spanner , כך ששאילתה של SPANNER_IS_MANAGED בתצוגה INDEXES מחזירה את כל האינדקסים לגיבוי.

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';

מהי הפעולה הרפרנציאלית שמוגדרת עם אילוץ המפתח הזר?

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

האם המפתח הזר נאכף או לא נאכף?

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';

מידע נוסף זמין במאמר סכימת מידע.

הסרת קשר של מפתח זר

ה-DDL הבא מסיר אילוץ של מפתח זר מהטבלה Orders.

ALTER TABLE Orders
  DROP CONSTRAINT FK_CustomerOrder;

האינדקסים לגיבוי של המפתח הזר נמחקים אוטומטית כשהאילוץ עצמו נמחק.

תמיכה בקשרים מורכבים יותר של מפתחות זרים

בנושאים הבאים מוסבר איך להשתמש במפתחות זרים כדי לאכוף קשרים מורכבים יותר בין טבלאות.

כמה עמודות

מפתחות זרים יכולים להפנות לכמה עמודות. רשימת העמודות יוצרת מפתח שתואם למפתח הראשי של הטבלה או לאינדקס לגיבוי. הטבלה המפנה מכילה מפתחות זרים של מפתח הטבלה שאליה מפנים.

בדוגמה הבאה, הגדרות המפתח הזר שנאכפות מציינות ש:

  • לכל ערך של SongName בטבלה TopHits צריך להיות ערך תואם בטבלה Songs.

  • לכל זוג ערכים של SingerFirstName ו-SingerLastName צריך להיות זוג ערכים תואם של FirstName ו-LastName בטבלה Singers.

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)
);

הפניות מעגליות

לפעמים יש בטבלאות תלויות מעגליות, אולי מסיבות שקשורות למערכות מדור קודם או בגלל דה-נורמליזציה. מפתחות זרים ב-Spanner מאפשרים הפניות מעגליות. מכיוון שהטבלה שאליה מפנים צריכה להיות קיימת לפני שמפתח זר יכול להפנות אליה, צריך להוסיף את אחד מהמפתחות הזרים באמצעות הצהרת ALTER TABLE. הנה דוגמה:

  1. יוצרים את TableA, בלי מפתח זר.
  2. יוצרים את TableB עם אילוץ של מפתח זר ב-TableA.
  3. משתמשים ב-ALTER TABLE ב-TableA כדי ליצור הפניה למפתח זר אל TableB.

טבלאות עם הפניה עצמית

סוג מיוחד של הפניה מעגלית הוא טבלה שמגדירה מפתח זר שמפנה לאותה טבלה. לדוגמה, קטע הקוד הבא מציג מפתח זר כדי לאכוף ש-ManagerId של עובד הוא גם עובד.

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)
);

המאמרים הבאים