Oracle: Nicht schlüsselerhaltende Tabelle sollte sein

7

Ich erhalte die Meldung "ORA-01779: Eine Spalte, die einer nicht schlüsselerhaltenen Tabelle zugeordnet ist, kann nicht geändert werden", wenn ich versuche, einen Join zu aktualisieren. Ich habe mich auf der Website umgesehen und viele Ratschläge gefunden, was Schlüsselerhaltung bedeutet und warum dies notwendig ist ... aber so nah ich das beurteilen kann, halte ich mich an diesen Rat und erhalte immer noch den Fehler.

Ich habe zwei Tabellen:

PG_LABLOCATION has, among other things, the columns:
"LABLOCID" NUMBER,
"DNSNAME" VARCHAR2(200 BYTE)

LABLOCID is the primary key, DNSNAME has a unique constraint

PG_MACHINE has, among other things, the columns:
"MACHINEID" NUMBER, 
"LABLOCID" NUMBER, 
"IN_USE" NUMBER(1,0) DEFAULT 0, 
"UPDATE_TIME" TIMESTAMP (6) DEFAULT '01-JAN-1970'

MACHINEID is a primary key
LABLOCID is a foreign key into LABLOCID in PG_LABLOCATION (its primary key)

Das Update, das ich ausführe, ist:

update 
  (select mac.in_use, mac.update_time
     from pg_machine mac 
     inner join pg_lablocation loc
       on mac.lablocid = loc.lablocid
     where loc.dnsname = 'value'
       and '02-JAN-2013' > mac.update_time
  )
set in_use = 1 - MOD( 101, 2 ), update_time = '02-JAN-2013';

Ich aktualisiere nur Werte in einer Tabelle (PG_MACHINE) und die Verknüpfungsspalte in der anderen Tabelle ist der Primärschlüssel, wodurch der Schlüssel beim Lesen erhalten bleibt. Ich bin besorgt, dass die where-Klausel das Problem verursacht, aber ich habe versucht, den Filter auf mac.update_time zu entfernen, und habe den gleichen Fehler erhalten, und loc.dnsname hat eine eindeutige Einschränkung.

Was noch seltsamer ist, ist, dass wir, wie viele Leute, eine Entwickler- und eine Produktumgebung haben. Wir haben eine vollständige Schema- und Datenmigration von prod zu dev durchgeführt. Ich habe sie beide durchgesehen und sie haben identische Indizes und Einschränkungen. Die Abfrage funktioniert in dev, generiert jedoch den obigen Fehler in prod.

Also zwei Fragen:

1) Können Sie sehen, was mit meiner Anfrage nicht stimmt? 2) Können Sie vorschlagen, was zwischen meiner Entwickler- und Produktumgebung unterschiedlich sein könnte (z. B. Servereinstellungen), was diesen Fehler in der einen, aber nicht in der anderen verursachen könnte?

rauben
quelle
2
Kein eindeutiger Index für PG_MACHINE.LABLOCID?
igr
Ja da ist. Mea culpa; Ich habe versucht, die Beschreibung des Problems auf die Länge des Forums zu reduzieren und zu tief zu schneiden.
Rob

Antworten:

9

Sie können einen Join in Oracle aktualisieren, wenn die folgenden Bedingungen erfüllt sind :

  1. Es wird nur eine Basistabelle aktualisiert
  2. Alle anderen Tabellen sind schlüsselerhalten : Jede von ihnen muss höchstens eine Zeile für jede Zeile der Basistabelle enthalten.

(Es gelten zusätzliche Einschränkungen für die Aktualisierung von Ansichten. )

In Ihrem Beispiel aktualisieren Sie PG_MACHINEnur die Tabelle . Oracle muss sicherstellen, dass für eine einzelne Zeile dieser Tabelle nur eine Zeile der anderen gefunden werden kann. Dies scheint der Fall zu sein, da Sie eine PK aktiviert haben PG_LABLOCATION.LABLOCID. Daher sollten Sie in der Lage sein, den Join zu aktualisieren. Siehe zum Beispiel diese SQLFiddle mit einem ähnlichen Setup .

In Ihrem Fall sollten Sie entweder:

  • Stellen Sie sicher, dass der Primärschlüssel aktiviert, validiert und nicht aufschiebbar ist (interessanterweise verhindert eine aufschiebbare Einschränkung, dass Oracle den Join aktualisiert!).
  • Verwenden Sie MERGEif , wenn PG_LABLOCATION.LABLOCIDes für die entsprechende Abfrage eindeutig ist. MERGEist weniger streng als das Aktualisieren mit Joins und gibt nur dann einen Fehler zurück, wenn die Ergebnismenge tatsächlich ein Duplikat enthält (während dies UPDATEfehlschlägt, wenn die Möglichkeit eines Duplikats besteht).
  • Überprüfen Sie Ihre Abfrage, da Sie in der SELECTKlausel keine Werte aus der übergeordneten Tabelle benötigen , können Sie sie als Semi-Join umschreiben (dies garantiert, dass kein Duplikat generiert wird):

    UPDATE (SELECT mac.in_use, mac.update_time
              FROM pg_machine mac
             WHERE mac.lablocid IN (SELECT loc.lablocid 
                                      FROM pg_lablocation loc 
                                     WHERE loc.dnsname = 'value')
               AND to_date('02-JAN-2013') > mac.update_time)
       SET in_use = 1 - MOD(101, 2), 
           update_time = to_date('02-JAN-2013');

    Dies könnte wie folgt umgeschrieben werden:

    UPDATE pg_machine mac
       SET in_use = 1 - MOD(101, 2), 
           update_time = to_date('02-JAN-2013')
     WHERE mac.lablocid IN (SELECT loc.lablocid 
                              FROM pg_lablocation loc 
                             WHERE loc.dnsname = 'value')
       AND to_date('02-JAN-2013') > mac.update_time;

In diesem Fall würde ich mich für die dritte Option entscheiden: Im Allgemeinen können Sie die Eltern in einem Eltern-Kind-Join nicht aktualisieren .

Vincent Malgrat
quelle
Danke für den Hinweis! Leider habe ich dich in die Irre geführt; PG_MACHINE.LABLOCID hat eine eindeutige Einschränkung, die ich beim Schreiben nicht abgefangen und erwähnt habe. Aber ich habe trotzdem versucht, Ihr Semi-Join neu zu schreiben - ich hatte mich vor dieser Struktur gescheut und gedacht, es sei eine korrelierte Unterabfrage, aber das ist es natürlich nicht - und es hat gut funktioniert. Ich würde immer noch sehr gerne wissen, warum es in unserer Entwicklungsumgebung anders läuft als in prod und warum die einzigartige Einschränkung den Tabellenschlüssel nicht erhalten lässt, aber ich werde mich mit "Es funktioniert jetzt" und zufrieden geben sei glücklich.
Rob
(Die Konvertierung von Varchar in Datum ist nur ein Ergebnis davon, dass ich das Problem auf das Wesentliche für das Forum reduziert habe. Der reale Fall befindet sich in einem Trigger und ersetzt 101, 'Wert' und das Datum an beiden Stellen durch Variablen des entsprechenden Typs Aber ich schätze den Rat trotzdem.) (Ich entschuldige mich auch dafür, dass ich Ihre Antwort nicht positiv bewertet habe; anscheinend bin ich nicht seriös genug.)
Rob
1
@rob Ich wurde mit Eltern-Kind verwechselt. Ihr Update sollte erfolgreich sein. Sehen Sie sich dieses SQLFiddle- Beispiel an. Sie benötigen nicht einmal eine eindeutige Einschränkung für PG_MACHINE.LABLOCID!! Die Fehlermeldung kann von einer aufschiebbaren Einschränkung in einer Umgebung stammen, siehe diese andere SQLFiddle . Über die Datumskonvertierung können Sie diese Notation für wörtliche Datumsangaben verwenden =)
Vincent Malgrat