Postgres DISTINCT与DISTINCT ON

tek*_*eee 14 sql postgresql

我有一个使用以下语句创建的Postgres表.此表由另一个服务的数据转储填充.

CREATE TABLE data_table (
    date date DEFAULT NULL,
    dimension1 varchar(64) DEFAULT NULL,
    dimension2 varchar(128) DEFAULT NULL
) TABLESPACE pg_default;
Run Code Online (Sandbox Code Playgroud)

我正在构建的ETL中的一个步骤是提取它们的唯一值dimension1并将它们插入另一个中间表中.但是,在某些测试中,我发现下面的2个命令不会返回相同的结果.我希望两者都返回相同的金额.与第二个命令相比,第一个命令返回的结果更多(1466行与1504行相比).

-- command 1
SELECT DISTINCT count(dimension1)
FROM data_table;

-- command 2    
SELECT count(*)
FROM (SELECT DISTINCT ON (dimension1) dimension1
FROM data_table
GROUP BY dimension1) AS tmp_table;
Run Code Online (Sandbox Code Playgroud)

有什么明显的解释吗?作为解释的替代方案,是否有任何建议检查我应该做的数据?

编辑:以下查询都返回1504(与"简单"相同DISTINCT)

SELECT count(*)
FROM data_table WHERE dimension1 IS NOT NULL;

SELECT count(dimension1)
FROM data_table;
Run Code Online (Sandbox Code Playgroud)

谢谢!

dmg*_*dmg 19

DISTINCT和DISTINCT ON具有完全不同的语义.

首先是理论

DISTINCT适用于整个元组.一旦计算出查询结果,DISTINCT就会从结果中删除任何重复的元组.

例如,假设表R具有以下内容:

#table r;
a | b 
---+---
1 | a
2 | b
3 | c
3 | d
2 | e
1 | a
Run Code Online (Sandbox Code Playgroud)

(6排)

从R中选择distinct*将导致:

# select distinct * from r;
 a | b 
---+---
 1 | a
 3 | d
 2 | e
 2 | b
 3 | c
(5 rows)
Run Code Online (Sandbox Code Playgroud)

请注意,distinct适用于整个投影属性列表:因此

select distinct * from R
Run Code Online (Sandbox Code Playgroud)

在语义上等同于

select distinct a,b from R
Run Code Online (Sandbox Code Playgroud)

你不能发出

select a, distinct b From R
Run Code Online (Sandbox Code Playgroud)

DISTINCT必须遵循SELECT.它适用于整个元组,而不适用于结果的属性.

DISTINCT ON是该语言的postgresql补充.分组相似但不完全相同.

它的语法是:

 SELECT DISTINCT ON (attributeList) <rest as any query>
Run Code Online (Sandbox Code Playgroud)

例如:

 SELECT DISTINCT ON (a) * from R
Run Code Online (Sandbox Code Playgroud)

它的语义可以描述如下.像往常一样计算查询,但在结果投影之前,根据DISTINCT ON中的属性列表对当前结果进行排序并对其进行分组(类似于分组依据).现在,使用每个组中的第一个元组进行投影,并忽略其他元组.

例:

 SELECT DISTINCT on (a) * from r;
  a | b 
 ---+---
 1 | a
 2 | b
 3 | c
 (3 rows)
Run Code Online (Sandbox Code Playgroud)

现在,回到你的问题:

第一个查询:

SELECT DISTINCT count(dimension1)
FROM data_table;
Run Code Online (Sandbox Code Playgroud)

计算dimension1的计数(data_table中元组的数量,其中dimension1不为null).此查询返回一个元组,该元组始终是唯一的(因此DISTINCT是冗余的).

查询2:

SELECT count(*)
FROM (SELECT DISTINCT ON (dimension1) dimension1
FROM data_table
GROUP BY dimension1) AS tmp_table;
Run Code Online (Sandbox Code Playgroud)

这是查询中的查询.让我为了清楚起见重写它:

WITH tmp_table AS (
   SELECT DISTINCT ON (dimension1) 
     dimension1 FROM data_table
     GROUP by dimension1) 
SELECT count(*) from tmp_table
Run Code Online (Sandbox Code Playgroud)

让我们先计算tmp_table.正如我上面提到的,让我们首先忽略DISTINCT ON并执行剩下的查询.这是维度1的分组.因此,查询的这一部分将导致每个不同维度值1的一个元组.

现在,DISTINCT ON.它再次使用dimension1.但是dimension1已经是唯一的(由于分组).因此,这使得DISTINCT ON superflouos(它什么都不做).最终计数只是该组中所有元组的计数.

如您所见,以下查询中存在等价(它适用于具有属性a的任何关系):

SELECT (DISTINCT ON a) a
FROM R
Run Code Online (Sandbox Code Playgroud)

SELECT a FROM R group by a
Run Code Online (Sandbox Code Playgroud)

SELECT DISTINCT a FROM R
Run Code Online (Sandbox Code Playgroud)

警告

对于任何给定的数据库实例,使用DISTINCT ON结果查询可能是不确定的.换句话说,查询可能会为相同的表返回不同的结果.

一个有趣的方面

Distinct ON 以更清晰的方式模拟sqlite和mysql 的不良行为.假设R有两个属性a和b:

SELECT a, b FROM R group by a
Run Code Online (Sandbox Code Playgroud)

是SQL中的非法语句.然而,它运行在mysql和sqlite上.它只是从a的相同值组中的任何元组中获取b的随机值.在Postgresql中,这种说法是非法的.相反,您必须使用DISTINCT ON并写入:

SELECT DISTINCT ON (a) a,b from R
Run Code Online (Sandbox Code Playgroud)

*推论*

当您想要访问功能上依赖于属性组的值时,DISTINCT ON在组中很有用.换句话说,如果您知道对于每组属性,它们始终具有第三个属性的相同值,则对该组属性使用DISTINCT.否则,您必须创建一个JOIN来检索第三个属性.