web-dev-qa-db-ja.com

Oracle:非キー保存テーブルは

結合を更新しようとすると、「ORA-01779:キーが保存されていないテーブルにマップする列を変更できません」というメッセージが表示されます。サイトを検索したところ、キー保持の意味とそれが必要な理由について多くのアドバイスが見つかりました...しかし、私が知る限り、そのアドバイスを順守しているにもかかわらず、エラーが発生します。

2つのテーブルがあります。

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)

私が実行している更新は:

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

私は1つのテーブル(PG_MACHINE)の値のみを更新しており、もう1つのテーブルの結合列は主キーです。これにより、読み取りによってキーが保持されます。 where句が問題を引き起こしているのではないかと心配していますが、mac.update_timeでフィルターを削除しようとすると同じエラーが発生し、loc.dnsnameには一意の制約があります。

さらに奇妙なのは、多くの人々のように、開発環境と製品環境があるということです。 prodからdevへの完全なスキーマとデータの移行を行いました。私はそれらの両方を調べましたが、それらには同じインデックスと制約があります。クエリはdevで機能しますが、prodで上記のエラーを生成します。

したがって、2つの質問:

1)私のクエリのどこが悪いのか分かりますか? 2)私の開発環境と本番環境の違い(サーバー設定など)によって、一方ではこのエラーが発生するが、他方では発生しない可能性があることを示唆できますか?

7
Rob

次の条件を満たす場合 の場合、Oracleの結合を更新できます。

  1. 基本テーブルは1つだけ更新されます
  2. 他のすべてのテーブルは key-preserved です。各テーブルには、ベーステーブルの各行に対して最大で1行が必要です。

(追加 ビューの更新に関する制限 が適用されます)

あなたの例では、テーブルPG_MACHINEのみを更新します。 Oracleは、このテーブルの1つの行に対して、もう1つの行しか見つからないことを確認する必要があります。 PG_LABLOCATION.LABLOCIDにPKがあるため、これが当てはまるようです。したがって、結合を更新できるはずです。たとえば、この SQLFiddleと同様の設定 を参照してください。

あなたの場合、あなたはどちらかをするべきです:

  • 主キーが有効になっていて、検証されていることを確認してください 延期不可 (興味深いことに、延期可能な制約により、Oracleは結合を更新できません!)
  • 使用する MERGEPG_LABLOCATION.LABLOCIDが関連するクエリに対して一意である場合。 MERGEは結合による更新よりも厳密ではなく、結果セットに実際に重複がある場合にのみエラーを返します(UPDATE重複の可能性がある場合は失敗します)。
  • クエリを確認します。SELECT句の親テーブルの値は必要ないため、準結合として書き換えることができます(これにより、重複が生成されないことが保証されます)。

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

    これは次のように書き直すことができます。

    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;
    

この場合、3番目のオプションを使用します。通常親子結合で親を更新することはできません。

9
Vincent Malgrat