Oracle: Bảng không được bảo quản khóa phải là


7

Tôi đang nhận được "ORA-01779: không thể sửa đổi một cột ánh xạ tới bảng không được bảo tồn khóa" khi tôi cố cập nhật liên kết. Tôi đã tìm kiếm trên trang web và tìm thấy rất nhiều lời khuyên về ý nghĩa của việc bảo quản khóa và tại sao cần thiết ... nhưng gần như tôi có thể nói rằng tôi đang tuân thủ lời khuyên đó và vẫn nhận được lỗi.

Tôi có hai bảng:

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)

Bản cập nhật tôi đang chạy là:

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

Tôi chỉ cập nhật các giá trị trong một bảng (PG_MACHINE) và cột tham gia trong bảng khác là khóa chính, điều này sẽ làm cho khóa được bảo toàn bằng cách đọc của tôi. Tôi lo ngại rằng mệnh đề where gây ra sự cố, nhưng tôi đã thử xóa bộ lọc trên mac.update_time và gặp cùng một lỗi và loc.dnsname có một ràng buộc duy nhất.

Điều thậm chí kỳ quặc là chúng ta có, giống như nhiều người, một nhà phát triển và môi trường prod. Chúng tôi đã thực hiện một lược đồ hoàn chỉnh và di chuyển dữ liệu từ prod sang dev. Tôi đã xem cả hai và họ có các chỉ số và ràng buộc giống hệt nhau. Truy vấn hoạt động trong dev, nhưng tạo ra lỗi trên trong prod.

Vì vậy, hai câu hỏi:

1) Bạn có thể thấy những gì sai với truy vấn của tôi? 2) Bạn có thể đề xuất những gì có thể khác nhau giữa môi trường dev và prod của tôi (ví dụ: cài đặt máy chủ) có thể gây ra lỗi này ở một nhưng không phải lỗi khác không?


2
Không có chỉ mục duy nhất trên PG_MACHINE.LABLOCID?
igr

Có, có. Mea culpa; Tôi đã cố gắng giảm bớt phần mô tả vấn đề theo chiều dài diễn đàn và cắt quá sâu.
Cướp

Câu trả lời:


9

Bạn có thể cập nhật tham gia vào Oracle nếu đáp ứng các điều kiện sau :

  1. Chỉ có một bảng cơ sở được cập nhật
  2. Tất cả các bảng khác được bảo tồn khóa : mỗi bảng phải có tối đa một hàng cho mỗi hàng của bảng cơ sở.

( áp dụng các hạn chế bổ sung về cập nhật lượt xem )

Trong ví dụ của bạn, bạn chỉ cập nhật bảng PG_MACHINE. Oracle phải đảm bảo rằng đối với một hàng của bảng này, chỉ có thể tìm thấy một hàng khác. Đây có vẻ là trường hợp kể từ khi bạn có PK PG_LABLOCATION.LABLOCID. Do đó, bạn sẽ có thể cập nhật tham gia. Xem ví dụ SQLFiddle này với một thiết lập tương tự .

Trong trường hợp của bạn, bạn nên:

  • đảm bảo rằng khóa chính được bật, xác thực, không thể bảo vệ được (điều thú vị là, một ràng buộc có thể bảo vệ sẽ ngăn Oracle cập nhật liên kết!)
  • sử dụng MERGEnếu PG_LABLOCATION.LABLOCIDlà duy nhất cho các truy vấn có liên quan. MERGEít nghiêm ngặt hơn so với cập nhật với các phép nối và sẽ chỉ trả về lỗi nếu thực sự có một bản sao trong tập kết quả (trong khi đó UPDATEsẽ thất bại nếu có khả năng trùng lặp).
  • xem lại truy vấn của bạn, vì bạn không cần các giá trị từ bảng cha trong SELECTmệnh đề, bạn có thể viết lại nó dưới dạng bán tham gia (đảm bảo rằng sẽ không tạo ra trùng lặp):

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

    Điều này có thể được viết lại như sau:

    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;

Trong trường hợp này tôi sẽ đi với tùy chọn thứ ba: nói chung, bạn không thể cập nhật cha mẹ trong một tham gia cha-con .


Cảm ơn vì lời khuyên! Thật không may, tôi đã dẫn bạn lạc lối; PG_MACHINE.LABLOCID có một ràng buộc duy nhất đối với nó, điều mà tôi không thể bắt và đề cập khi viết nó lên. Nhưng tôi đã thử viết lại bằng cách tham gia bán lại của bạn - tôi đã tránh xa cấu trúc đó vì nghĩ rằng đó là một truy vấn phụ tương quan, nhưng tất nhiên là không - và nó đã có hiệu quả. Tôi vẫn rất muốn biết lý do tại sao nó chạy khác trong môi trường dev của chúng tôi so với prod và tại sao ràng buộc duy nhất không làm cho khóa bảng được bảo tồn, nhưng tôi chắc chắn sẽ giải quyết cho "Nó hoạt động ngay bây giờ" và Hãy hạnh phúc.
Cướp

(Biến đổi thành ngày thay đổi chỉ là kết quả của việc tôi loại bỏ vấn đề xuống bản chất của nó cho diễn đàn; trường hợp thực tế nằm trong trình kích hoạt và thay thế 101, 'giá trị' và ngày ở cả hai vị trí với các biến có loại phù hợp Nhưng tôi đánh giá cao lời khuyên như nhau.) (Ngoài ra, xin lỗi vì đã không nâng cao phản hồi của bạn; rõ ràng tôi không đủ uy tín.)
Rob

1
@rob Tôi đã nhầm lẫn với cha mẹ-con. Cập nhật của bạn sẽ thành công, hãy xem ví dụ SQLFiddle này , bạn thậm chí không cần một ràng buộc duy nhất nào PG_MACHINE.LABLOCID!! Thông báo lỗi có thể đến từ một ràng buộc có thể bảo vệ trong một môi trường, xem SQLFiddle khác này . Về chuyển đổi ngày, bạn có thể sử dụng ký hiệu này cho ngày theo nghĩa đen =)
Vincent Malgrat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.