Kann sich ein Fremdschlüssel auf einen Primärschlüssel in derselben Tabelle beziehen?

72

Ich denke nur, dass die Antwort falsch ist, weil der Fremdschlüssel keine uniquenessEigenschaft hat.

Aber einige Leute sagten, dass es im Falle eines Selbsteintritts in den Tisch sein kann. Ich bin neu in SQL. Wenn es wahr ist, erklären Sie bitte wie und warum?

Employee table
| e_id | e_name  | e_sala  |  d_id  |
|----  |-------  |-----    |--------|
|  1   |   Tom   |  50K    |    A   |
|  2   | Billy   |  15K    |    A   |
|  3   | Bucky   |  15K    |    B   |


department table
| d_id | d_name  |
|----  |-------  |
|  A   |   XXX   | 
|  B   |   YYY   | 

Jetzt ist d_id ein Fremdschlüssel, also wie es ein Primärschlüssel sein kann. Und etwas darüber erklären join. Was nützt es?

Eines Mannes
quelle
Möglicherweise möchten Sie hinzufügen, welches DBMS Sie verwenden, damit Sie Beispiele erhalten, die für Ihr DBMS funktionieren.
a_horse_with_no_name

Antworten:

93

Ich finde die Frage etwas verwirrend.

Wenn Sie meinen "Kann sich ein Fremdschlüssel auf einen Primärschlüssel in derselben Tabelle beziehen?", Lautet die Antwort ein festes Ja, wie einige geantwortet haben. Beispielsweise kann in einer Mitarbeitertabelle eine Zeile für einen Mitarbeiter eine Spalte zum Speichern der Mitarbeiternummer des Managers enthalten, in der der Manager auch ein Mitarbeiter ist, und daher eine Zeile in der Tabelle wie eine Zeile eines anderen Mitarbeiters.

Wenn Sie meinen "Kann eine Spalte (oder ein Satz von Spalten) sowohl ein Primärschlüssel als auch ein Fremdschlüssel in derselben Tabelle sein?", Lautet die Antwort meiner Ansicht nach ein Nein. es scheint bedeutungslos. Die folgende Definition ist jedoch in SQL Server erfolgreich!

create table t1(c1 int not null primary key foreign key references t1(c1))

Aber ich denke, es ist sinnlos, eine solche Einschränkung zu haben, wenn nicht jemand ein praktisches Beispiel findet.

AmanS kann in Ihrem Beispiel d_id unter keinen Umständen ein Primärschlüssel in der Employee-Tabelle sein. Eine Tabelle kann nur einen Primärschlüssel haben. Ich hoffe, das klärt Ihren Zweifel. d_id ist / kann nur in der Abteilungstabelle ein Primärschlüssel sein.

mvsagar
quelle
Mir ist klar, dass d_id der Primärschlüssel der Abteilungstabelle ist. Auch d_id in der Mitarbeitertabelle ist ein Fremdschlüssel. und Sie erwähnen auch, dass d_id der Primärschlüssel in derselben Tabelle (Mitarbeiter) sein kann. Mein Lehrer sagte mir, dass es im Falle eines Selbstbeitritts zum Mitarbeitertisch möglich sein kann. Dieser Punkt ist mir nicht klar, wie?
AmanS
1
@AmanS, Sie sagten "Sie erwähnen, dass d_id Primärschlüssel in derselben Tabelle (Mitarbeiter) sein kann". Aber ich habe es nicht getan; Hier ist meine Aussage "AmanS, in Ihrem Beispiel kann d_id unter keinen Umständen ein Primärschlüssel in der Employee-Tabelle sein". Hoffe du bekommst es. Dies ist auch im Self-Join-Fall nicht möglich. Eine andere Spalte in einer Tabelle kann auf den Primärschlüssel derselben Tabelle verweisen. Beispiel: Tabellenmitarbeiter erstellen (e_id int Primärschlüssel, e_name varchar (30), e_mgr int, Fremdschlüssel (e_mgr) verweist auf Mitarbeiter (e_id)). Dies ist ein Self-Join-Fall, und e_mgr ist ein Fremdschlüssel, der auf den Primärschlüssel e_id verweist.
Mvsagar
Können Sie mir ein praktisches Beispiel mit einer Abfrage nennen, in der Fremd auf Primärschlüssel verweisen kann? aber auch hier sagte die Person unten (ryvantage), dass es kann ... ich bin verwirrt, bitte helfen Sie
AmanS
@AmanS, ich habe bereits in meinem vorherigen Kommentar ein Beispiel für eine Mitarbeitertabelle angegeben, und ryvantage hat auch ein Beispiel für eine Personentabelle mit Daten angegeben. Dies sind praktische Beispiele. Sie lesen jedes Buch über Datenbanksysteme und lesen diese Kommentare noch einmal, und ich hoffe, Sie werden es bekommen. Es gibt nichts Schöneres als einen Fremdschlüssel, der auf einen Primärschlüssel in einer Abfrage verweist (DML-Anweisung). Die Referenz ist nur in der DDL-Anweisung CREATE TABLE oder der ALTER TABLE-Anweisung vorhanden. Und meine DDL-Anweisung CREATE TABLE-Mitarbeiter ... in meinem früheren Kommentar zeigt es.
Mvsagar
2
@AmanS, das ist es, wonach Sie wahrscheinlich suchen. Die select-Anweisung "SELECT DISTINCT e.e_id AS 'Manager-ID', e.e_name AS 'Manager-Name' FROM Mitarbeiter e, Mitarbeiter m WHERE e.e_id = m.e_mgr;" gibt Ihnen alle Mitarbeiter, die Manager sind. Hier wird die Primärschlüsselspalte e_id mit der Fremdschlüsselspalte e_mgr verglichen.
Mvsagar
17

Sicher warum nicht? Angenommen , Sie haben einen haben PersonTisch, mit id, name, age, und parent_id, wo parent_idein Fremdschlüssel für die gleiche Tabelle. Sie müssten die PersonTabelle nicht auf Parentund ChildTabellen normalisieren , das wäre übertrieben.

Person
| id |  name | age | parent_id |
|----|-------|-----|-----------|
|  1 |   Tom |  50 |      null |
|  2 | Billy |  15 |         1 |

Etwas wie das.

Ich nehme an, um die Konsistenz aufrechtzuerhalten, müsste es jedoch mindestens 1 Nullwert für geben parent_id. Die eine "Alpha-Männchen" -Reihe.

EDIT: Wie die Kommentare zeigen, hat Sam einen guten Grund gefunden, dies nicht zu tun. Es scheint, dass in MySQL, wenn Sie versuchen, Änderungen am Primärschlüssel vorzunehmen, die Bearbeitung CASCADE ON UPDATEnicht ordnungsgemäß weitergegeben wird , selbst wenn Sie angeben . Obwohl Primärschlüssel (normalerweise) für die Bearbeitung in der Produktion gesperrt sind, ist dies dennoch eine Einschränkung, die nicht ignoriert werden darf. Daher ändere ich meine Antwort auf: - Sie sollten diese Vorgehensweise wahrscheinlich vermeiden, es sei denn, Sie haben eine ziemlich strenge Kontrolle über das Produktionssystem (und können garantieren, dass niemand eine Kontrolle implementiert, die die PKs bearbeitet). Ich habe es nicht außerhalb von MySQL getestet.

Ryvantage
quelle
siehe nur meine bearbeitete Frage mit Beispiel. ist deine Antwort immer noch ja?
AmanS
1
Es funktioniert nicht für update. Überprüfen Sie sqlfiddle.com/#!9/445052/1/0 und versuchen Update menus set id = 6 WHERE id = 1;Sie dann hinzuzufügen, dass Sie erhalten#1451 - Cannot delete or update a parent row
Sam
@ Sam zwei Dinge: 1) Ich sehe, dass es nicht funktioniert, aber ich bin nicht einverstanden, dass es fehlschlagen sollte. Bei einem kaskadierenden Update scheint es keinen logischen Grund zu geben, warum das Update fehlschlagen sollte. 2) Wer ändert PKs in praktischen Situationen? Leute, die um Ärger bitten lol
ryvantage
@ryvantage Stimmen Sie voll und ganz zu, dass PK in praktischen Situationen nicht aktualisiert wird. Ich wollte nur diesen Kommentar hinzufügen, um zu erwähnen, dass dies eine bekannte dokumentierte Einschränkung (oder ein Fehler ) in MySQL ist.
Sam
Ich sehe ON UPDATE CASCADEwird in der Bearbeitung erwähnt. Ich würde davon abraten, [ON UPDATE CASCADE] [ON DELETE CASCADE]generell zu verwenden, da die Kaskadenaktion keine Trigger auslöst . Wenn Sie Trigger haben, die bei einer Aktualisierung in Tabelle A ausgelöst werden sollen, und Sie eine Aktualisierung in Tabelle B durchführen, die zu einer kaskadierenden Änderung in A führt, werden Trigger in Tabelle A nicht ausgelöst. Verwenden Sie ON UPDATE RESTRICTstattdessen und behandeln Sie das erforderliche Löschen manuell in der Anwendungslogik.
Adrian Wiik
13

Dies kann ein gutes Erklärungsbeispiel sein

CREATE TABLE employees (
id INTEGER NOT NULL PRIMARY KEY,
managerId INTEGER REFERENCES employees(id), 
name VARCHAR(30) NOT NULL
);

INSERT INTO employees(id, managerId, name) VALUES(1, NULL, 'John');
INSERT INTO employees(id, managerId, name) VALUES(2, 1, 'Mike');

- Erläuterung: - In diesem Beispiel. - John ist Mikes Manager. Mike verwaltet niemanden. - Mike ist der einzige Mitarbeiter, der niemanden verwaltet.

Γιώργος Τερζής
quelle
8

Beispiel: n Unterkategorieebene für Kategorien. Die Primärschlüssel- ID der folgenden Tabelle wird durch die Fremdschlüssel- Unterkategorie_ID bezeichnet

Geben Sie hier die Bildbeschreibung ein

Nimya V.
quelle
1

Ein gutes Beispiel für die Verwendung von IDs anderer Zeilen in derselben Tabelle wie Fremdschlüssel sind verschachtelte Listen.

Durch Löschen einer Zeile mit untergeordneten Elementen (dh Zeilen, die sich auf die ID des Elternteils beziehen), die auch untergeordnete Elemente haben (dh auf IDs von untergeordneten Elementen verweisen), wird eine Kaskade von Zeilen gelöscht.

Dies erspart viel Schmerz (und viel Code, was mit Waisen zu tun ist - dh Zeilen, die sich auf nicht vorhandene IDs beziehen).

bbe
quelle