it-swarm.com.de

Oracle: Nicht schlüsselerhaltende Tabelle sollte sein

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 Entwicklungs- und Produktumgebung unterschiedlich sein könnte (z. B. Servereinstellungen), was diesen Fehler in der einen, aber nicht in der anderen verursachen könnte?

7
Rob

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

  1. Es wird nur eine Basistabelle aktualisiert
  2. Alle anderen Tabellen sind Schlüssel beibehalten : Jede von ihnen muss höchstens eine Zeile für jede Zeile der Basistabelle haben.

(zusätzliche Einschränkungen beim Aktualisieren von Ansichten gelten)

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

In Ihrem Fall sollten Sie entweder:

  • stellen Sie sicher, dass der Primärschlüssel aktiviert, validiert, nicht aufschiebbar ist (interessanterweise verhindert eine aufschiebbare Einschränkung, dass Oracle den Join aktualisiert!).
  • verwenden Sie MERGE , wenn PG_LABLOCATION.LABLOCID für die entsprechende Abfrage eindeutig ist. MERGE ist weniger streng als das Aktualisieren mit Joins und gibt nur dann einen Fehler zurück, wenn tatsächlich ein Duplikat in der Ergebnismenge vorhanden ist (während UPDATE fehlschlägt, wenn es vorhanden ist ist die Möglichkeit eines Duplikats).
  • überprüfen Sie Ihre Abfrage, da Sie in der SELECT -Klausel 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 .

9
Vincent Malgrat