2012-03-21 78 views
15

The MSDN claims that the order is:Thứ tự đúng của việc chèn/xóa/sửa đổi trên tập dữ liệu là gì?

  1. Bảng con: xóa bản ghi.
  2. Bảng chính: chèn, cập nhật và xóa bản ghi.
  3. Bảng con: chèn và cập nhật các bản ghi.

Tôi gặp vấn đề với điều đó.

Ví dụ: ParentTable có hai hồ sơ parent1 (Mã sản phẩm: 1) và parent2 (Mã sản phẩm: 2)

ChildTable có một child1 kỷ lục (Mã sản phẩm: 1, ParentId: 1)

Nếu chúng tôi cập nhật child1 để có một parent2 mới, và sau đó chúng ta xóa parent1.

  1. Chúng tôi không có gì để xóa trong bảng con
  2. Chúng tôi xóa parent1: chúng tôi đã phá vỡ hạn chế, bởi vì con vẫn còn dính với parent1, trừ khi chúng tôi cập nhật nó đầu tiên.

Vì vậy, đúng thứ tự là gì và MSDN có sai về chủ đề này không?

personnals những suy nghĩ của tôi là

  1. bảng Child: xóa các bản ghi.
  2. Bảng chính: chèn, cập nhật hồ sơ.
  3. Bảng con: chèn và cập nhật các bản ghi.
  4. Bảng cha: xóa bản ghi.

Nhưng vấn đề là, với ràng buộc có khả năng duy nhất, chúng tôi luôn phải xóa các bản ghi trong bảng trước khi thêm mới ... Vì vậy, tôi không có giải pháp ngay bây giờ để cam kết dữ liệu của tôi vào cơ sở dữ liệu của tôi.

Chỉnh sửa: cảm ơn câu trả lời, nhưng trường hợp góc của bạn là trường hợp hàng ngày của tôi ... Tôi chọn giải pháp xấu cho giới hạn bị vô hiệu hóa, sau đó cập nhật cơ sở dữ liệu và hạn chế kích hoạt lại. Tôi vẫn đang tìm kiếm một giải pháp tốt hơn ..

+0

là máy chủ sql này? Hoặc tôi đã bỏ lỡ thẻ. Câu hỏi thú vị +1 – cctan

+0

Đây là SqlServer 2008, nhưng tôi nghĩ nó khá độc lập DBMS! –

+0

Tôi nghĩ rằng trẻ em thay đổi bố mẹ là một hoàn cảnh bất thường. –

Trả lời

4

Sản phẩm SQL của bạn có hỗ trợ kiểm tra hạn chế bị trì hoãn không?

Nếu không, bạn có thể thử

Xóa tất cả hồ sơ trẻ em - xóa tất cả hồ sơ mẹ - chèn tất cả hồ sơ mẹ - chèn tất cả các con hồ sơ

nơi bất kỳ bản cập nhật đã được chia thành xóa và chèn thành phần của chúng.

Điều này sẽ hoạt động chính xác trong mọi trường hợp, nhưng ở tốc độ chấp nhận có thể là không ...

Nó cũng chứng minh rằng đây là chương trình duy nhất có thể hoạt động chính xác trong mọi trường hợp, vì:

(a) khó khăn chính về mẹ dictate rằng xóa mẹ phải đặt trước chèn phụ huynh,
(b) quan trọng những hạn chế về con dictate đứa trẻ đó xóa phải đặt trước chèn đứa trẻ,
(c) FK dictates rằng con xóa phải đặt trước mẹ xóa
(d) FK cũng dictates rằng chèn con phải làm theo cha mẹ chèn

Trình tự cho là chỉ có thể có một thỏa mãn đó là 4 yêu cầu, và nó cũng cho thấy rằng UPDATEs cho đứa trẻ làm cho một giải pháp không thể không có vấn đề gì, kể từ khi một UPDATE có nghĩa là một "đồng thời" DELETE cộng INSERT.

+0

Điểm rất thú vị, tôi không bao giờ thấy bản cập nhật dưới dạng bản chèn + bản cập nhật, nhưng biết tôi hiểu tại sao tôi đang ở trong một kết thúc chết. Đối với hiệu suất, tôi thích để hạn chế vô hiệu hóa, nhưng tôi giữ ý tưởng của bạn chia tách cập nhật trong hai. –

+0

Cảm ơn bạn đã trả lời! Tôi đang đấu tranh với vấn đề tương tự. Đề xuất của bạn có khả năng có thể làm việc cho tôi, nhưng thay thế UPDATE bằng DELETE và INSERT có thể có các tác dụng phụ không mong muốn, giống như xóa tầng của một số trẻ em. Bạn có bất kỳ ý tưởng mới kể từ khi bạn đã trả lời này? – suryadeva

3

Âm thanh với tôi như:

  1. Chèn parent2. Con vẫn trỏ đến parent1.
  2. Cập nhật con để trỏ tới parent2. Bây giờ không có gì liên quan đến parent1.
  3. Xóa parent1.

Bạn muốn kết hợp nó trong giao dịch nếu có.

Tùy thuộc vào sơ đồ của bạn, bạn cũng có thể mở rộng này để:

  1. Cập nhật parent1 để chỉ ra rằng nó đã bị khoá (hoặc khóa nó trong DB), do đó ngăn ngừa cập nhật.
  2. Chèn parent2
  3. Cập nhật con trỏ đến parent2
  4. Xóa parent1

Lệnh này có ưu điểm là một tham gia giữa cha mẹ và con sẽ trả về một kết quả phù hợp trong suốt. Khi trẻ đang cập nhật kết quả của một lần tham gia sẽ "lật" sang trạng thái mới.

EDIT:

lựa chọn khác là để di chuyển các tài liệu tham khảo cha/con vào bảng khác, ví dụ "liên kết";

CREATE TABLE links (
    link_id INT NOT NULL IDENTITY(1,1) PRIMARY KEY, 
    parent_id INT NOT NULL, 
    child_id INT NOT NULL 
); 

Bạn cũng có thể muốn các ràng buộc khóa ngoài trên cột mẹ và cột con, như tất nhiên là một số chỉ số thích hợp. Sự sắp xếp này cho phép các mối quan hệ rất linh hoạt giữa các bảng cha và con - có thể quá linh hoạt, nhưng điều đó phụ thuộc vào ứng dụng của bạn. Bây giờ bạn có thể làm một cái gì đó như;

UPDATE links 
    SET parent_id = @new_parent_id 
    WHERE parent_id = @old_parent_id 
    AND child_id = @child_id; 
0

Tôi nghĩ rằng cách chia các thao tác trên bàn không phải là một thiết kế tốt, vì vậy giải pháp của tôi là

  1. chèn/cập nhật/xóa bảng cha
  2. chèn/cập nhật/xóa bảng con

điểm quan trọng là bạn không nên thay đổi parentId của một bản ghi con, bạn nên xóa con của parent1 và thêm một con mới cho parent2. bằng cách làm như thế này bạn sẽ không còn lo lắng về sự ràng buộc bị phá vỡ nữa. và tất nhiên bạn phải sử dụng giao dịch.

+0

Hãy tưởng tượng một bảng người dùng, chỉ trên một bảng quốc gia. Tôi không thể xóa hàng người dùng mỗi khi người dùng muốn thay đổi quốc gia của mình ... (Đừng trả lời tôi rằng tôi sẽ không xóa quốc gia, đây không phải là điểm :)) –

+0

Tôi nghĩ bạn không cần để cập nhật bảng quốc gia khi thay đổi quốc gia của người dùng. vì vậy bạn không cần phải xóa người dùng :). trường hợp này chỉ xảy ra trên bảng con chính cho tôi, không phải bảng tra cứu. –

1

Cần xóa một bản ghi gốc mà không xóa các bản ghi con là không bình thường đến mức tôi chắc chắn đơn đặt hàng thông thường của các tập dữ liệu được xác định bởi MS không áp dụng trong trường hợp này.

Phương pháp hiệu quả nhất sẽ là CẬP NHẬT các bản ghi con để phản ánh phụ huynh mới, sau đó XÓA phụ huynh gốc. Như những người khác đã đề cập, hoạt động này nên được thực hiện trong một giao dịch.

4

Bạn phải tính đến ngữ cảnh của chúng. MS nói

Khi cập nhật bảng liên quan trong một tập dữ liệu, điều quan trọng là để cập nhật trong chuỗi thích hợp để giảm thiểu nguy cơ vi phạm referential ràng buộc toàn vẹn.

trong ngữ cảnh viết phần mềm ứng dụng dữ liệu khách hàng.

Tại sao điều quan trọng là giảm cơ hội vi phạm ràng buộc toàn vẹn tham chiếu?Bởi vì vi phạm những trở ngại có nghĩa là nhiều

  • chuyến đi vòng giữa các DBMS và khách hàng, hoặc cho các mã khách hàng để xử lý các hành vi vi phạm chế, hoặc cho người sử dụng con người để xử lý các vi phạm,
  • thêm thời gian thực hiện,
  • hơn tải trên máy chủ,
  • nhiều cơ hội hơn cho lỗi của con người, và
  • nhiều cơ hội để cập nhật đồng thời để thay đổi dữ liệu cơ bản (có thể gây nhầm lẫn hoặc mã ứng dụng, người sử dụng con người, hoặc cả hai).

Và tại sao họ xem xét thủ tục của họ đúng cách? Bởi vì nó cung cấp một quá trình duy nhất sẽ tránh vi phạm toàn vẹn tham chiếu trong hầu như tất cả các trường hợp phổ biến, và thậm chí trong rất nhiều trường hợp không phổ biến. Ví dụ . . .

  • Nếu bản cập nhật là một hoạt động DELETE trên bảng tham chiếu, và nếu phím nước ngoài trong các bảng tham khảo được khai báo là ON DELETE CASCADE, thì điều tối ưu là chỉ cần xóa các dòng tham chiếu (hàng mẹ) và để các dbms quản lý thác. (Đây cũng là điều tối ưu cho ON DELETE SET DEFAULT, và cho ON DELETE SET NULL.)

  • Nếu cập nhật là thao tác DELETE trên bảng được tham chiếu, và nếu khóa ngoài trong bảng tham chiếu được khai báo là ON DELETE RESTRICT, sau đó điều tối ưu là xóa tất cả các hàng tham chiếu (hàng con) trước, sau đó xóa hàng được tham chiếu.

Nhưng, với việc sử dụng thích hợp các giao dịch, thủ tục của MS để cơ sở dữ liệu ở trạng thái nhất quán bất kể. Giá trị là nó là một quá trình phía máy khách duy nhất để mã hóa và duy trì, mặc dù nó không phải là tối ưu trong mọi trường hợp. (Đó là thường xảy ra trong thiết kế phần mềm - lựa chọn một cách đơn đó không phải là tối ưu trong mọi trường hợp ActiveRecord nhảy trong tâm trí..)

Bạn nói

Ví dụ: ParentTable có hai hồ sơ parent1 (Mã sản phẩm: 1) và parent2 (Id : 2)

ChildTable có một child1 kỷ lục (Mã sản phẩm: 1, ParentId: 1)

Nếu chúng tôi cập nhật child1 để có một parent2 mẹ mới và chúng tôi xóa parent1.

  1. Chúng tôi không có gì để xóa trong bảng con
  2. Chúng tôi xóa parent1: chúng tôi đã phá vỡ hạn chế, bởi vì con vẫn còn dính với parent1, trừ khi chúng tôi cập nhật nó đầu tiên.

Đó không phải là vấn đề toàn vẹn tham chiếu; đó là một vấn đề về thủ tục. Vấn đề này rõ ràng đòi hỏi hai giao dịch.

  1. Cập nhật trẻ để có cha mẹ mới, sau đó cam kết. Dữ liệu này phải được sửa chữa bất kể điều gì xảy ra với phụ huynh đầu tiên.Cụ thể, dữ liệu này phải được sửa chữa ngay cả khi có các cập nhật đồng thời hoặc các ràng buộc khác khiến cho nó tạm thời hoặc vĩnh viễn không thể xóa cha mẹ đầu tiên. (Đây không phải là một vấn đề toàn vẹn tham chiếu, vì không có BẬT DELETE ĐỂ TỚI PHỤ HUYNH TIẾP THEO HAY MAKE mệnh đề GUESS TỐT NHẤT của bạn trong các ràng buộc khóa ngoài của SQL.)

  2. Xóa cha mẹ đầu tiên, sau đó cam kết. Điều này có thể yêu cầu đầu tiên cập nhật bất kỳ số hàng con nào trong bất kỳ số lượng bảng nào. Trong một tổ chức lớn, tôi có thể tưởng tượng một số lần xóa như thế này mất vài tuần để hoàn thành.

+0

muốn tôi có thể cung cấp cho điều này nhiều hơn một upvote! – user158017

0

Xác nhận quyền sở hữu MSDN là chính xác dựa trên việc sử dụng phụ thuộc (khóa ngoại). Hãy nghĩ đến những thứ tự như

  1. bảng Child (cascade xóa)
  2. bảng phụ huynh: chèn và/hoặc cập nhật và/hoặc xóa kỷ lục có nghĩa là bước cuối cùng của thác xóa.
  3. Bảng con: chèn hoặc cập nhật.

Vì chúng tôi nói về xóa tầng, chúng tôi phải đảm bảo rằng bằng cách xóa bản ghi gốc, cần xóa mọi bản ghi con liên quan đến cha mẹ trước khi chúng tôi xóa bản ghi gốc. Nếu chúng tôi không có hồ sơ trẻ em, không có xóa ở cấp độ trẻ em. Đó là tất cả.

Mặt khác, bạn có thể tiếp cận trường hợp của bạn theo nhiều cách khác nhau. Tôi nghĩ rằng một cuộc sống thực tế (gần như) kịch bản sẽ hữu ích hơn. Giả sử rằng bảng cha là phần chính của các đơn đặt hàng (orderID, clientID, vv) và bảng con là phần chi tiết (detailID, orderID, productOrServiceID, v.v.). Vì vậy, bạn nhận được một trật tự và bạn đã sau

bảng Chánh

orderID = 1 (auto increment) 
... 

bảng Child

detailID = 1 (auto increment) 
orderID = 1 
productOrServiceID = 342 

and 

detailID = 2 
orderID = 1 
productOrServiceID = 169 

and 

detailID = 3 
orderID = 1 
productOrServiceID = 307 

Vì vậy, chúng tôi có một đơn hàng cho ba sản phẩm/dịch vụ. Bây giờ khách hàng của bạn muốn bạn chuyển sản phẩm hoặc dịch vụ thứ hai sang một đơn đặt hàng mới và phân phối nó sau này. Bạn có hai tùy chọn để thực hiện việc này.

Người đầu tiên (trực tiếp)

  • Tạo đơn hàng mới (kỷ lục mẹ mới) mà được OrderID = 2

  • bảng Cập nhật con bằng cách thiết lập OrderID = 2 nơi OrderID = 1 và productOrServiceID = 169

kết quả là bạn sẽ có

Chánh bảng

orderID = 1 (auto increment) 
... 

and 

orderID = 2 
... 

bảng Child

detailID = 1 (auto increment) 
orderID = 1 
productOrServiceID = 342 

and 

detailID = 2 
orderID = 2 
productOrServiceID = 169 

and 

detailID = 3 
orderID = 1 
productOrServiceID = 307 

thứ hai (gián tiếp)

  • Giữ một DataRow của sản phẩm/dịch vụ thứ hai từ bảng con như là một biến

  • Xóa hàng tương đối khỏi bảng con

  • Tạo đơn hàng mới (kỷ lục mẹ mới) mà được OrderID = 2

  • Chèn DataRow giữ trên bảng con bằng cách thay đổi OrderID trường từ 1 để 2

Kết quả là bạn sẽ có

bảng Chánh

orderID = 1 (auto increment) 
... 

and 

orderID = 2 
... 

bảng Child

detailID = 1 (auto increment) 
orderID = 1 
productOrServiceID = 342 

and 

detailID = 3 
orderID = 1 
productOrServiceID = 307 

and 

detailID = 4 
orderID = 2 
productOrServiceID = 169 

Lý do cho tùy chọn thứ hai, đó là theo cách thích hợp nhất cho nhiều ứng dụng, là cung cấp các chuỗi thô chi tiết cho từng bản ghi gốc. Tôi đã thấy các trường hợp mở rộng tùy chọn thứ hai bằng cách tạo lại tất cả các bản ghi chi tiết. Tôi nghĩ rằng đó là khá dễ dàng để tìm giải pháp nguồn mở liên quan đến trường hợp này và kiểm tra việc thực hiện.

Cuối cùng lời khuyên cá nhân của tôi là tránh thực hiện loại công cụ này với bộ dữ liệu trừ khi ứng dụng của bạn là người dùng đơn lẻ. Cơ sở dữ liệu có thể dễ dàng xử lý "vấn đề" này trong một chủ đề an toàn với giao dịch.

Các vấn đề liên quan