7

我有一个带有 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 中的数据的结果集,前提是其中一个展位 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_B , ID_C,ID_D是表的外键table_b, table_c,table_d

4

4 回答 4

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
        ;
于 2013-05-25T11:01:30.007 回答
3

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

Table_B.ID还给出了和的索引Table_C.ID

如果匹配情况Table_A很少(小于 ~ 5 %,取决于行和数据分布),则部分多列索引将有助于提高性能:

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)可能会有所帮助——在这种情况下,您将在索引中包含所有感兴趣的列(在我的示例中没有)。取决于几个因素,例如表中的行宽和更新频率。

于 2013-05-25T18:38:32.170 回答
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进行标量子查询缓存(如 Oracle 所做的那样),那么嵌套选择可能会在您有大量数据重复的情况下有所帮助Table_A

于 2013-05-25T09:32:30.947 回答
0

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

RDBMS 可以很好地处理单个复杂的语句,但它们在处理许多小查询时会遇到困难。例如,如果您在一个查询中查询“一”和“多关系”,这将比在 1 + n 条选择语句中执行此操作要快。

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

至于您的空值比较,它可能表明您的数据模型可以优化,但这只是一个猜测。很有可能您可以改进设计,使这些列中不允许出现 null。

于 2013-05-25T11:16:00.493 回答