我在 PostgreSQL 13 数据库中有两个表
table_a
和 table_b
,具有 UUID 主键列。
table_a
有多个条目,而 table_b
是空的(没有条目)。
以下查询返回预期结果,即 entry_count_a
大于 0
:
SELECT COUNT(DISTINCT ta.uuid) AS entry_count_a FROM table_a ta
但是,以下查询为两个 entry_counts 返回
0
:
SELECT COUNT(DISTINCT ta.uuid) AS entry_count_a, COUNT(DISTINCT tb.uuid) AS entry_count_b FROM table_a ta, table_b tb
编写查询的正确方法是什么,以便
entry_count_a
包含正确的(预期)值 > 0
,而 entry_count_b
是 0
?
额外问题:为什么 Postgres 会这样?
您当前的查询等同于以下查询:
SELECT COUNT(DISTINCT ta.uuid) AS entry_count_a,
COUNT(DISTINCT tb.uuid) AS entry_count_b
FROM table_a ta
CROSS JOIN table_b tb
当您在两个表之间应用笛卡尔积时,您将它们的基数相乘。您没有任何行,因为两个表之一的基数为 0,因此对于任何 n,0*n 始终为 0。
如果要正确显示这两个计数,可以使用如下两个子查询:
SELECT
(SELECT COUNT(DISTINCT uuid) FROM table_a) AS entry_count_a,
(SELECT COUNT(DISTINCT uuid) FROM table_b) AS entry_count_b
在这里查看演示.
您的查询将生成笛卡尔积表 a x 表 b,因为您没有建立您想要关联它们的方式。通常,我们使用 WHERE 条件或 JOIN 子句。因此,如果 A 中有 n 行 x B 中有 0 行,则 SQL 语句的结果将是 0 行。 在这种情况下,最简单的方法是运行两个单独的查询,每个表一个,或者像这样使用子查询:
SELECT COUNT(DISTINCT ta.uuid) AS entry_count_a, SELECT COUNT(DISTINCT tb.uuid) FROM table_b tb) AS entry_count_b FROM table_a ta
我现在使用的解决方法:
SELECT a.count,
b.count FROM
(SELECT COUNT(DISTINCT uuid) AS count FROM table_a) a,
(SELECT COUNT(DISTINCT uuid) AS count FROM table_b) b
Count 0 是由于
CROSS JOIN
中隐含的 FROM table_a ta, table_b tb
,其中一张空表抵消了另一张。
加入是错误的方法,正如其他答案已经指出的那样。 (除了手头的副作用,构建笛卡尔积对于大表来说非常昂贵。改为运行两个单独的计数。
因为
uuid
是每个表中的PK,所以使用count(*)
:
SELECT (SELECT count(*) FROM table_a) AS count_a
, (SELECT count(*) FROM table_b) AS count_b
;
count(DISTINCT uuid)
将是一个很大的浪费,因为 uuid
根据定义是 UNIQUE
。
count(uuid)
仍然是浪费,因为根据定义,该列也是 NOT NULL
。 Postgres 有一个单独的、更快的实现count(*)
,简单地计算行数。
关于错位连接:
关于更快的计数(*):
使用
CROSS JOIN
的另一种方法:
with cte_table_a as (
SELECT COUNT(DISTINCT uuid) AS entry_count_a FROM table_a
),
cte_table_b as (
SELECT COUNT(DISTINCT uuid) AS entry_count_b FROM table_b
)
select * from cte_table_a
cross join cte_table_b
或者简单地说:
SELECT *
from (SELECT COUNT(DISTINCT uuid) AS entry_count_a FROM table_a) a
CROSS JOIN (SELECT COUNT(DISTINCT uuid) AS entry_count_b FROM table_b) b