空表上的 Postgres 意外行为

问题描述 投票:0回答:5

我在 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 会这样?

sql postgresql count cross-join
5个回答
2
投票

您当前的查询等同于以下查询:

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

在这里查看演示.


2
投票

您的查询将生成笛卡尔积表 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

1
投票

我现在使用的解决方法:

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

1
投票

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(*)
,简单地计算行数。

关于错位连接:

关于更快的计数(*):


0
投票

使用

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

这里演示

© www.soinside.com 2019 - 2024. All rights reserved.