2012-02-12 55 views
9

Như đã trả lời trong câu hỏi này: Cardinality in PostgreSQL, cardinality được enfforced sử dụng constraints.PostgreSQL: Làm thế nào để thực hiện cardinality tối thiểu?

Quy tắc số lượng thẻ xác định số lượng cho phép của các mối quan hệ - một, nhiều, nhiều, v.v. Nhiều người có thể đạt được bằng cách sử dụng bàn nối và một-nhiều bằng cách sử dụng khóa NGOẠI HỐI.

Nhưng làm cách nào người ta có thể thực hiện mối quan hệ một-một-một-một-một (một-một-1). Điều tương tự như để hỏi: Làm thế nào tôi có thể thực thi cardinality tối thiểu trong PostgreSQL?

Tình huống thực tế là nơi cần lưu trữ địa chỉ nói (hoặc số điện thoại) phải được cung cấp (nhưng có thể nhiều hơn một) bởi người đó (nói người dùng hoặc khách hàng).

Chỉnh sửa:

Tình huống được đề cập ở trên là trường hợp đặc biệt (với thẻ chung) của một vấn đề chung. Vấn đề chung là: Làm thế nào để thực thi cardinality của số tùy ý?

answered by jug a không null Tham chiếu KEY FOREIGN có thể được sử dụng như một công việc xung quanh, nếu thẻ tối thiểu là một. Nó cũng sẽ cung cấp một tính năng bổ sung để chọn mặc định trong số nhiều.

Nhưng xem xét một tình huống của mối quan hệ giữa đội ngũ Cricketcầu thủ của mình. Mỗi đội phải có MINIMUM trong số 11 người chơi để đủ điều kiện làm một đội. Ở đây, số lượng cardinality tối thiểu là mười một (11).

Tương tự, một mối quan hệ giữa một khóa học sinh viên trong một trường học, nơi mỗi học sinh phải ghi danh AT-ÍT NHẤT 5 khóa học và mỗi khóa học phải có tối thiểu là 10 sinh viên.

+1

Cả hai ví dụ được thêm đều hiển thị cùng chất lượng. Làm thế nào để bạn bắt đầu xây dựng một đội cricket từ đầu? Làm thế nào để một sinh viên ghi danh vào khóa học đầu tiên của mình? Sinh viên có ít hơn 5 khóa học phải được phép tồn tại, ít nhất là tạm thời. Vì vậy, đây không phải là ** một nơi tốt để sử dụng các ràng buộc. Sử dụng truy vấn, chức năng hoặc trình kích hoạt cho mục đích. –

Trả lời

3

Không có cách nào để xác định này sử dụng một hạn chế CHECK, vì vậy tôi nghĩ rằng cách tiếp cận tốt nhất là một kích hoạt:

http://www.postgresql.org/docs/9.1/static/sql-createtrigger.html
http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html

Bạn muốn kết thúc với một cái gì đó tương tự (Tôi có không thử nghiệm nó hoặc bất cứ điều gì):

CREATE TRIGGER at_least_one before INSERT, UPDATE, DELETE ON the_one_table FOR EACH ROW EXECUTE PROCEDURE check_at_least_one(); 

CREATE OR REPLACE FUNCTION check_at_least_one() RETURNS trigger AS $$ 
    BEGIN 
    nmany := select count(*) from the_many_table where the_many_table.the_one_id=NEW.id; 
    IF nmany > 0 THEN 
     RETURN NEW; 
    END IF; 
    RETURN NULL; 
END; 
+0

INSERT ban đầu được thực hiện như thế nào? Vì bảng ONE phải được chèn đầu tiên (để đảm bảo khóa ngoại được tham chiếu tồn tại), thì hàng đầu tiên trong bảng MANY có thể được chèn vào tham chiếu đến bảng ONE. Nhưng kích hoạt của bạn sẽ cấm điều này. –

+0

Trả lời bản thân mình: bằng cách sử dụng hai (!!) 'CONSTRAINT' gây nên' INITIALLY DEFERRED'. Những trình kích hoạt này không được trả về 'NULL' nhưng sử dụng' RAISE EXCEPTION'. Kích hoạt đầu tiên xem bảng "một", bảng kia "nhiều" (để bắt xóa sau giao dịch đầu tiên). –

1

Nếu bạn có địa chỉ trong một bảng adresses, bạn có thể xác định một cột "defau lt_address "(trong bảng khách hàng) là số không rỗng tham chiếu khóa ngoài tới địa chỉ phải được cung cấp.

Nếu bạn có một bảng shippings cung cấp một tùy chọn mối quan hệ nhiều-nhiều bằng cách tham khảo một người, một địa chỉ và có thể một trật tự (-item), sau đó bạn có thể sử dụng liên hiệp để ghi đè lên NULLs cho các địa chỉ bạn nhận được trong một phép nối ngoài giữa (khách hàng bên tham gia lệnh) và shipping_addresses (một cái nhìn tham gia shippings với địa chỉ).Tuy nhiên, để ngăn chặn vấn đề với những con số có thể khác nhau của các thành phần không null địa chỉ, Stephane Faroult khuyến cáo trong cuốn sách của ông (khuyên!) The Art of SQL sử dụng "ẩn chìa khóa loại" kỹ thuật (giả định rằng customers_with_default_address là một cái nhìn tham gia khách hàng với địa chỉ sử dụng "DEFAULT_ADDRESS":.

select * 
    from (select 1 as sortkey, 
       line_1, 
       line_2, 
       city, 
       state, 
       postal_code, 
       country 
     from shipping_addresses 
      where customer_id = ? 
     union 
     select 2 as sortkey, 
       line_1, 
       line_2, 
       city, 
       state, 
       postal_code, 
       country 
     from customers_with_default_address 
      where customer_id = ? 
     order by 1) actual_shipping_address 
     limit 1 
+0

Ah, ý tưởng của cột _default_address_ là thực tế và chỉ có ý nghĩa nếu một người muốn thực thi số lượng thẻ tối thiểu của một (1). Những gì tôi trình bày là _a tình huống thực tế, như một phần của câu hỏi chỉ là một trong nhiều tình huống có thể xảy ra. Hãy xem xét một tình huống khác về mối quan hệ giữa nhóm [Cricket] (http://en.wikipedia.org/wiki/Cricket) và các cầu thủ của nó. Mỗi đội phải có MINIMUM trong số 11 người chơi để đủ điều kiện làm một đội. Ở đây, số lượng cardinality tối thiểu là mười một (11). – user1144616

+0

@ user1144616: Vui lòng thêm ví dụ này vào chính câu hỏi, nó mô tả vấn đề rất rõ ràng. –

+0

@ A.H .: Đã thêm, cùng với một ví dụ khác. – user1144616

3

không có cách nào để thực thi quy tắc như vậy chỉ sử dụng FOREIGN KEY chế

1) Một cách là bằng cách cho phép tham chiếu vòng tròn giữa các bảng (cột "mặc định", được khuyên bởi bình). Điều này dẫn đến các vấn đề về trứng và trứng khó quản lý, bạn sẽ phải sử dụng các ràng buộc khó khăn. Ngoài ra, tùy chọn này chỉ đơn giản là không có sẵn trong một số DBMS. Một bất lợi khác là đối với một đội bóng đá, bạn sẽ phải thêm 11 cột "mặc định" (và bạn sẽ phải đối phó với vấn đề về gà và 11 trứng)!

2) Một tùy chọn khác là sử dụng trình kích hoạt.

3) Một tùy chọn khác là sử dụng hạn chế mức cơ sở dữ liệu giữa 2 bảng. Không chắc chắn nếu có bất kỳ DBMS nào có chức năng như vậy. Bên cạnh các ràng buộc điển hình là UNIQUE, PRIMARYFOREIGN KEY, hầu hết DBMS chỉ có các ràng buộc mức hàng và có giới hạn (không có truy vấn phụ, v.v ...).

4) Một tùy chọn khác là thực thi các quy tắc như vậy bằng cách tạo các thủ tục INSERT, UPDATE và DELETE thích hợp chỉ có thể truy cập hai bảng có liên quan và thực thi tính toàn vẹn theo các quy tắc này. Đây là cách tiếp cận tốt hơn (theo ý kiến ​​của tôi).

5) Một tùy chọn khác, thực hiện đơn giản hơn là sử dụng các ràng buộc khóa ngoài tiêu chuẩn, thực thi mối quan hệ 1-nhiều và có Chế độ xem hiển thị các Nhóm thực sự có 11 người chơi trở lên. Khóa học này có nghĩa là bạn không thực sự thực thi các quy tắc mà bạn yêu cầu. Nhưng nó có thể (và tôi có thể nói có thể xảy ra) mà bạn có thể đủ khả năng không quá. Nếu các cầu thủ bóng đá bị giết trong một tai nạn chẳng hạn, đội bóng không còn có thể chơi trong các giải đấu nữa nhưng nó vẫn là một đội. Vì vậy, bạn có thể xác định hai thực thể, Nhóm (Bảng cơ sở) và ProperTeam (Xem), có thể chơi trò chơi. Ví dụ:

CREATE VIEW AS ProperTeam 
(SELECT * 
    FROM Team 
    WHERE (SELECT COUNT(*) 
      FROM Player 
      WHERE Player.TeamId = Team.TeamId 
     ) >= 11 
) 

Tùy chọn 1 và 2 nhìn khá "lộn xộn" nhưng đó chỉ là một ý kiến ​​cá nhân, nhiều người như gây nên.

tôi sẽ chọn Tùy chọn 4, trừ khi tôi có thể ("ăn gian" và) thực sự không thực thi các hạn chế với Lựa chọn 5.

+0

Tùy chọn 5 và 4 là tốt từ khung nhìn cơ sở dữ liệu, nhưng hầu hết các khung công tác bền vững chung ở phía máy khách không thể đối phó với chúng - chúng đơn giản mong đợi 'INSERT',' UPDATE', 'SELECT' đơn giản cho một thực thể. Nói "làm sáng tạo với thủ tục này, cập nhật với người khác và tìm nạp dữ liệu thông qua chế độ xem" - vui chơi ;-) –

+2

@ A.H .: Đó là một ví dụ tại sao hầu hết các khung công tác bền bỉ thường là nạng ác. Lời hứa của họ là đơn giản hóa mọi thứ, hoặc thậm chí để làm cho ứng dụng của bạn trở thành bất khả tri (không bao giờ thực sự hoạt động). Một cách hiệu quả, chúng khóa bạn vào khung làm việc, bản thân nó được đảm bảo là ít di động hơn SQL thuần túy và cắt bỏ một nửa các tính năng của RDBMS của bạn. –

+1

@ErwinBrandstetter: Các khuôn khổ kiên trì giống như thuế: Rất ít người như họ, nhiều người ghét họ, (gần như) không ai có thể né tránh chúng trong thời gian dài. : -] –

1

Phương pháp mà làm việc cho tôi và yêu cầu một số tiền hợp lý của mã hóa là (dịch sang của bạn đội/máy nghe câu hỏi):

  • tạo deferrable nước ngoài hạn chế chủ chốt để thực thi các "mỗi người chơi có một đội" mối quan hệ.
  • chỉ tạo một trình kích hoạt cho nhóm bảng, kiểm tra rằng ít nhất n người chơi được gắn với nhóm. Kích hoạt nên ném một ngoại lệ nếu một cardinality không được tôn trọng, như chỉ ra bởi AdamKG.

này sẽ thực thi các hạn chế, nhưng là một tác dụng phụ này cũng sẽ cho phép chỉ có một cách để mã hóa một đội bóng mới của người chơi (có nghĩa là, mà không từ chối nó như là một sự vi phạm chính)

start transaction; 
set constraints all deferred; 
insert player_1 with teamPK --teamPK is not yet in table team, will be below 
... 
insert player_n with teamPK 
insert teamPK --this fires the trigger which is successful. 
commit transaction; --this fires the foreign key deferred check, successful. 

Lưu ý rằng tôi làm điều đó bằng cách sử dụng các khóa chính tự định nghĩa (cho teamPK), ví dụ một tên nhóm duy nhất, để tôi có thể biết teamPK trước khi thực sự chèn dòng vào nhóm bảng (trái ngược với việc sử dụng id tăng tự động).

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