2013-05-25 35 views
6

tôi có một cơ sở dữ liệu PostgreSQL với 4 bảng:bảng Gia nhập nếu tham chiếu tồn tại

Bảng A

--------------------------- 
| ID | B_ID | C_ID | D_ID | 
--------------------------- 
| 1 | 1 | NULL | NULL | 
--------------------------- 
| 2 | NULL | 1 | NULL | 
--------------------------- 
| 3 | 2 | 2 | 1 | 
--------------------------- 
| 4 | NULL | NULL | 2 | 
--------------------------- 

Bảng B

------------- 
| ID | DATA | 
------------- 
| 1 | 123 | 
------------- 
| 2 | 456 | 
------------- 

Bảng C

------------- 
| ID | DATA | 
------------- 
| 1 | 789 | 
------------- 
| 2 | 102 | 
------------- 

Bảng D

------------- 
| ID | DATA | 
------------- 
| 1 | 654 | 
------------- 
| 2 | 321 | 
------------- 

Tôi đang cố gắng để lấy một tập kết quả đó đã gia nhập dữ liệu từ bảng B và các dữ liệu từ bảng C, chỉ khi một trong những gian hàng ID không phải là null.

SELECT "Table_A"."ID", "Table_A"."ID_B", "Table_A"."ID_C", "Table_A"."ID_D", "Table_B"."DATA", "Table_C"."DATA" 
    FROM "Table_A" 
     LEFT JOIN "Table_B" on "Table_A"."ID_B" = "Table_B"."ID" 
     LEFT JOIN "Table_C" on "Table_A"."ID_C" = "Table_C"."ID" 
    WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; 

Điều này được khuyến nghị hay tôi nên chia nhỏ điều này trong nhiều truy vấn?

Có cách nào để thực hiện kết nối bên trong giữa các bảng này không?

Kết quả tôi mong đợi là:

------------------------------------------------- 
| ID | ID_B | ID_C | ID_D | DATA (B) | DATA (C) | 
------------------------------------------------- 
| 1 | 1 | NULL | NULL | 123 | NULL | 
------------------------------------------------- 
| 2 | NULL | 1 | NULL | NULL | 789 | 
------------------------------------------------- 
| 3 | 2 | 2 | NULL | 456 | 102 | 
------------------------------------------------- 

EDIT:ID_B, ID_C, ID_D là chìa khóa ngoại đến bảng table_b, table_c, table_d

+0

Tôi đã chỉnh sửa câu trả lời của mình và thêm kết quả tôi mong đợi. – wiizzard

+1

Bạn không hiển thị các ràng buộc FK. Bảng table_a.id_b và table_a.id_c FK có đến các bảng B và C không? (có vẻ như họ đang có, nhưng điều này không được đề cập trong câu hỏi) – wildplasser

+0

Bạn nói đúng. Họ là FK. – wiizzard

Trả lời

5

các WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; có thể được thay thế bằng các khoản tương ứng trên Bảng B và C: WHERE "Table_B"."ID" IS NOT NULL OR "Table_C"."ID" IS NOT NULL;. Điều này cũng sẽ làm việc nếu table_a.id_b và table_a.id_c không phải là FK cho các bảng B và C. Nếu không, một hàng table_a với {5, 5,5,5} sẽ lấy hai hàng NULL từ các bảng B và C.

SELECT ta."ID" AS a_id 
     , ta."ID_B" AS b_id 
     , ta."ID_C" AS c_id 
     , ta."ID_D" AS d_id 
     , tb."DATA" AS bdata 
     , tc."DATA" AS cdata 
FROM "Table_a" ta 
LEFT JOIN "Table_B" tb on ta."ID_B" = tb."ID" 
LEFT JOIN "Table_C" tc on ta."ID_C" = tc."ID" 
WHERE tb."ID" IS NOT NULL OR tc."ID" IS NOT NULL 
     ; 
+0

Tốt hơn :-). Ngoài truy vấn của bạn là một giải pháp tốt hơn trong trường hợp thiếu ràng buộc khoá ngoại, bạn có bất kỳ điểm chuẩn hiệu suất nào cho hai biến thể không? Tôi có xu hướng nghĩ rằng với các chỉ mục đặt trên khóa ngoài, truy vấn ban đầu sẽ nhanh hơn (và dễ đọc hơn), trừ khi người tối ưu hóa có thể thực hiện chuyển đổi cần thiết ... –

+0

Có, nó khác với truy vấn của OQ, ngoại trừ trong trường hợp trong đó ra.id_b và ta.id_c có các ràng buộc FK đối với các bảng B và C. Xem bình luận của tôi về OQ. Nếu không: ngữ nghĩa sẽ khác, kế hoạch sẽ khác, hiệu suất sẽ khác. (trong trường hợp đơn giản/nhỏ, hai hash-join sẽ được sử dụng, và sự khác biệt sẽ rất nhỏ) – wildplasser

2

Với yêu cầu của bạn, truy vấn của bạn có vẻ tốt để tôi. Một lựa chọn khác là sử dụng các lựa chọn lồng nhau trong phép chiếu, nhưng tùy thuộc vào dữ liệu, chỉ mục và ràng buộc của bạn, có thể chậm hơn, vì các lựa chọn lồng nhau thường dẫn đến vòng lặp lồng nhau, trong khi các phép nối có thể được thực hiện như kết hợp kết hợp hoặc lồng nhau bẹ:

SELECT 
    "Table_A"."ID", 
    "Table_A"."ID_B", 
    "Table_A"."ID_C", 
    "Table_A"."ID_D", 
    (SELECT "DATA" FROM "Table_B" WHERE "Table_A"."ID_B" = "Table_B"."ID"), 
    (SELECT "DATA" FROM "Table_C" WHERE "Table_A"."ID_C" = "Table_C"."ID") 
FROM "Table_A" 
WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; 

Nếu Postgres không scalar subquery caching (như Oracle không), lựa chọn sau đó lồng vào nhau có thể giúp đỡ trong trường hợp bạn có rất nhiều dữ liệu lặp lại trong Table_A

0

chung spealking cách đề nghị là để làm điều đó trong một truy vấn chỉ, và để cho các cơ sở dữ liệu làm càng nhiều việc càng tốt, đặc biệt là nếu bạn thêm các hoạt động khác như phân loại (thứ tự theo) hoặc pagination sau (giới hạn ... bù đắp ...) sau đó. Chúng tôi đã thực hiện một số phép đo và không có cách nào để sắp xếp/phân trang nhanh hơn trong Java/Scala, nếu bạn sử dụng bất kỳ bộ sưu tập cấp cao hơn nào như danh sách, v.v.

RDBMS xử lý rất tốt với các câu lệnh phức tạp duy nhất, nhưng chúng có khó khăn trong việc xử lý nhiều truy vấn nhỏ.Ví dụ, nếu bạn truy vấn "một" và "nhiều quan hệ" trong một truy vấn, nó sẽ nhanh hơn làm điều này trong các câu lệnh chọn 1 + n.

Đối với kết nối bên ngoài, chúng tôi đã thực hiện các phép đo và không có hình phạt thực sự thực tế nào so với kết nối bên trong. Vì vậy, nếu mô hình dữ liệu của bạn và/hoặc truy vấn của bạn yêu cầu một tham gia bên ngoài, chỉ cần làm điều đó. Nếu đó là một vấn đề hiệu suất, bạn có thể điều chỉnh nó sau này.

Đối với các so sánh không có giá trị, có thể cho biết mô hình dữ liệu của bạn có thể được tối ưu hóa, nhưng đó chỉ là phỏng đoán. Rất có thể là bạn có thể cải thiện thiết kế sao cho null không được phép trong các cột này.

+0

Conderning đoạn cuối cùng của bạn: nó phổ biến và hoàn toàn bình thường để có giá trị null trong các cột như vậy. Tôi không thấy làm thế nào mà sẽ chỉ ra một vấn đề. –

+0

@Erwin Đó là những gì tôi dự định mô tả. Vì tiếng Anh không phải là ngôn ngữ mẹ đẻ của tôi, tôi có thể cải thiện điều gì để làm rõ điều đó? – Beryllium

+0

Tiếng Anh của bạn có vẻ tốt. Tôi sẽ xóa đoạn cuối cùng. –

2

Vì bạn có các ràng buộc khóa ngoại, tại chỗ, tính toàn vẹn tham chiếu được đảm bảo và truy vấn trong Q của bạn là là câu trả lời hay nhất.

Cũng lập chỉ mục trên Table_B.IDTable_C.ID.

Nếu trường hợp tương ứng trong Table_Ahiếm (ít hơn ~ 5%, tùy thuộc vào hàng với và phân phối dữ liệu) một partial multi-column index sẽ giúp hiệu suất:

CREATE INDEX table_a_special_idx ON "Table_A" ("ID_B", "ID_C") 
WHERE "ID_B" IS NOT NULL OR "ID_C" IS NOT NULL; 

Trong PostgreSQL 9.2 một chỉ số bao phủ (index-only scan trong Postgres parlance) có thể giúp đỡ nhiều hơn - trong trường hợp đó bạn sẽ bao gồm tất cả các cột quan tâm trong chỉ mục (không phải trong ví dụ của tôi). Phụ thuộc vào một số yếu tố như chiều rộng hàng và tần suất cập nhật trong bảng của bạn.

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