如果引用存在,则连接表

时间:2013-05-25 09:25:56

标签: sql postgresql select join

我有一个包含4个表的PostgreSQL数据库:

表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

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

表C

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

表D

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

我正在尝试检索已加入表B中的数据和表C中的数据的结果集,但前提是其中一个booth ID不为空。

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;

这是推荐还是我应该在多个查询中将其拆分?

有没有办法在这些表之间进行内部联接?

我期望的结果是:

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

编辑: ID_BID_CID_D是表table_btable_c,{{1}的外键}}

4 个答案:

答案 0 :(得分:5)

WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL;可以替换为B和C表中的相应子句:WHERE "Table_B"."ID" IS NOT NULL OR "Table_C"."ID" IS NOT NULL;。如果table_a.id_b和table_a.id_c不是B和C表的FK,这也可以工作。否则,带有{5,5,5,5}的table_a行将从B和C表中检索两个NULL行。

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
        ;

答案 1 :(得分:2)

鉴于您的要求,您的查询对我来说似乎很好。

另一种方法是在投影中使用嵌套选择,但取决于您的数据,索引和约束,这可能会更慢,因为嵌套选择通常会导致嵌套循环,而连接可以作为合并连接或嵌套循环执行:

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;

如果Postgres确实scalar subquery caching(正如Oracle所做的那样),那么嵌套选择可能有助于您在Table_A

中重复大量数据

答案 2 :(得分:2)

由于您有外键约束,因此可以保证参照完整性,并且您的Q中的查询已经是最佳答案

还提供了Table_B.IDTable_C.ID上的索引。

Table_A中的匹配案例罕见(小于~5%,具体取决于行和数据分配)partial multi-column index帮助表现:

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;

在PostgreSQL 9.2中,覆盖索引(Postgres用语中的index-only scan)可能会有所帮助 - 在这种情况下,您将在索引中包含所有感兴趣的列(不在我的示例中)。取决于行宽和表中更新频率等几个因素。

答案 3 :(得分:0)

一般来说,推荐的方法就是在一个查询中执行,并让数据库尽可能多地工作,特别是如果你以后添加其他操作,如排序(order by)或分页(限制...抵消...)以后。我们已经完成了一些测量,如果您使用任何更高级别的集合(如列表等),则无法在Java / Scala中更快地进行排序/分页。

RDBMS处理单个复杂语句非常好,但它们在处理许多小查询时遇到困难。例如,如果在一个查询中查询“一个”和“多个关系”,它将比在1 + n个选择语句中执行此操作更快。

对于外连接,我们已经完成了测量,与内连接相比没有真正的性能损失。因此,如果您的数据模型和/或您的查询需要外部联接,那就去做吧。如果是性能问题,您可以稍后进行调整。

至于你的空比较,可能表明你的数据模型可以被优化,但这只是猜测。您可以改进设计,以便在这些列中不允许使用null。