Postgres DISTINCT vs DISTINCT ON

时间:2018-06-13 21:43:57

标签: sql postgresql

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

CREATE TABLE data_table (
    date date DEFAULT NULL,
    dimension1 varchar(64) DEFAULT NULL,
    dimension2 varchar(128) DEFAULT NULL
) TABLESPACE pg_default;

我正在构建的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;

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

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

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

SELECT count(dimension1)
FROM data_table;

谢谢!

4 个答案:

答案 0 :(得分:11)

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

首先是理论

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

例如,假设一个表R包含以下内容:

#table r;
a | b 
---+---
1 | a
2 | b
3 | c
3 | d
2 | e
1 | a

(6行)

从R中选择distinct *将导致:

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

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

select distinct * from R

在语义上等同于

select distinct a,b from R

您无法发出

select a, distinct b From R

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

DISTINCT ON 是该语言的postgresql补充。它与分组相似,但不完全相同。

它的语法是:

 SELECT DISTINCT ON (attributeList) <rest as any query>

例如:

 SELECT DISTINCT ON (a) * from R

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

示例:

 SELECT DISTINCT on (a) * from r;
  a | b 
 ---+---
 1 | a
 2 | b
 3 | c
 (3 rows)

现在,回到你的问题:

首先查询:

SELECT DISTINCT count(dimension1)
FROM data_table;

计算dimension1的计数(data_table中的元组数,其中dimension1不为null)。这个查询 返回一个元组,它总是唯一的(因此DISTINCT 是多余的。)

查询2:

SELECT count(*)
FROM (SELECT DISTINCT ON (dimension1) dimension1
FROM data_table
GROUP BY dimension1) AS tmp_table;

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

WITH tmp_table AS (
   SELECT DISTINCT ON (dimension1) 
     dimension1 FROM data_table
     GROUP by dimension1) 
SELECT count(*) from tmp_table

让我们先计算tmp_table。如上所述, 让我们首先忽略DISTINCT ON并完成其余部分 查询。这是维度1的分组。因此这部分查询 将导致每个维度值1的一个元组。

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

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

SELECT (DISTINCT ON a) a
FROM R

SELECT a FROM R group by a

SELECT DISTINCT a FROM R

警告

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

一个有趣的方面

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

SELECT a, b FROM R group by a

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

SELECT DISTINCT ON (a) a,b from R

*推论*

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

答案 1 :(得分:2)

第一个查询给出AlphaType.click(); AlphaType.findElement(By.xpath("//div[@id='home-tabs-pane-0']/div/div[4]/div[3]/div[2]/div/div/div/form/div[3]/div[2]/ul/li[3]/a/span")).click(); Thread.sleep(2000);的非空值的数量,而第二个查询返回列的不同值的数量。如果列包含重复项或空值,则这些数字显然不相等。

中的dimension1字样
DISTINCT

没有意义,因为查询返回单行。也许你想要

SELECT DISTINCT count(dimension1)
FROM data_table;

返回SELECT count(DISTINCT dimension1) FROM data_table; 的不同非空值的数量。请注意,它与

不同
dimension1

最后一个查询产生列的所有(空或非)不同值的数量。

答案 2 :(得分:0)

尝试

SELECT count(dimension1a)
FROM (SELECT DISTINCT ON (dimension1) dimension1a
FROM data_table
ORDER BY dimension1) AS tmp_table;

DISTINCT ON似乎与GROUP BY同义。

答案 3 :(得分:0)

学习和理解视觉例子会发生什么 这是在PostgreSQL上执行的一些SQL:

DROP TABLE IF EXISTS test_table;
CREATE TABLE test_table (
    id int NOT NULL primary key,
    col1 varchar(64) DEFAULT NULL
);

INSERT INTO test_table (id, col1) VALUES 
(1,'foo'), (2,'foo'), (3,'bar'), (4,null);

select count(*) as total1 from test_table;
-- returns: 4
-- Because the table has 4 records.

select distinct count(*) as total2 from test_table;
-- returns: 4
-- The count(*) is just one value. Making 1 total unique can only result in 1 total. 
-- So the distinct is useless here.

select col1, count(*) as total3 from test_table group by col1 order by col1;
-- returns 3 rows: ('bar',1),('foo',2),(NULL,1)
-- Since there are 3 unique col1 values. NULL's are included.

select distinct col1, count(*) as total4 from test_table group by col1 order by col1;
-- returns 3 rows: ('bar',1),('foo',2),(NULL,1)
-- The result is already grouped, and therefor already unique. 
-- So again, the distinct does nothing extra here.

select count(distinct col1) as total5 from test_table;
-- returns 2
-- NULL's aren't counted in a count by value. So only 'foo' & 'bar' are counted

select distinct on (col1) id, col1 from test_table order by col1 asc, id desc;
-- returns 3 rows: (2,'a'),(3,'b'),(4,NULL)
-- So it gets the records with the maximum id per unique col1
-- Note that the "order by" matters here. Changing that DESC to ASC would get the minumum id.

select count(*) as total6 from (select distinct on (col1) id, col1 from test_table order by col1 asc, id desc) as q;
-- returns 3. 
-- After seeing the previous query, what else would one expect?

select distinct col1 from test_table order by col1;
-- returns 3 unique values : ('bar'),('foo'),(null)

select distinct id, col1 from test_table order by col1;
-- returns all records. 
-- Because id is the primary key and therefore makes each returned row unique