我有一个用以下语句创建的postgres表。该表由来自另一个服务的数据的AS转储填充。
CREATE TABLE data_table (
date date DEFAULT NULL,
dimension1 varchar(64) DEFAULT NULL,
dimension2 varchar(128) DEFAULT NULL
) TABLESPACE pg_default;
我正在构建的ETL中的一个步骤是提取dimension1
的唯一值并将它们插入到另一个中间表中。然而,在一些测试中,我发现下面的两个命令返回的结果并不相同。我预计两者都会返还相同的金额。与第二个命令相比,第一个命令返回更多结果(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(与“Simple”DISTINCT
相同)
SELECT count(*)
FROM data_table WHERE dimension1 IS NOT NULL;
SELECT count(dimension1)
FROM data_table;
谢谢!
5条答案
按热度按时间huwehgph1#
DISTINCT和DISTINCT ON具有完全不同的语义。
首先是理论
DISTINCT应用于整个元组。一旦计算出查询结果,DISTINCT就会从结果中删除任何重复的元组。
例如,假设一个表R包含以下内容:
(6行)
SELECT DISTINCT*FOR R结果为:
请注意,DISTINCT适用于投影属性的整个列表:
在语义上等同于
你不能签发
DISTINCT必须跟在SELECT之后。它应用于整个元组,而不是结果的属性。
DISTINCT ON是对该语言的PostgreSQL添加。它与分组依据相似,但不完全相同。
其语法为:
例如:
IT语义可以描述如下。像往常一样计算--不包括DISTINCT ON(A)--但在结果投影之前,对当前结果进行排序,并根据DISTINCT ON中的属性列表对其进行分组(类似于GROUP BY)。现在,使用每组中的第一个元组进行投影,忽略其他元组。
示例:
然后,对于a的每个不同值(在本例中为1、2和3),取第一个元组。这与以下内容相同:
某些DBMS(尤其是SQLite)将允许您运行以下查询:
这会给你一个类似的结果。
当且仅当存在从a到b的函数依赖时,当且仅当存在从a到b的函数依赖时,PostgreSQL才允许该查询。换句话说,如果对于关系R的任何示例,每个值或a只有一个唯一的元组(因此选择第一个元组是确定性的:只有一个元组),则该查询将有效。
例如,如果R的主键是a,则a->b并且:
等同于:
现在,回到你的问题:
第一个查询:
计算Dimension1的计数(data_table中的元组数,其中Dimsion1不为空)。该查询返回一个元组,它始终是唯一的(因此DISTINCT是冗余的)。
问题2:
这是查询中的查询。为了清楚起见,让我重写一遍:
让我们先计算一下tmp_table。正如我在上面提到的,让我们首先忽略DISTINCT ON,并执行查询的其余部分。这是按维度1分组。因此,查询的这一部分将为每个不同的Dimension1值生成一个元组。
现在,不同的是。它再次使用了Dimsion1。但是维度1已经是唯一的(由于GROUP BY)。因此,这使得DISTINCT on Superflous(它什么都不做)。最后的计数只是GROUP BY中所有元组的计数。
如您所见,在以下查询中存在等价性(它适用于具有属性a的任何关系):
和
和
警告
对于给定的数据库示例,在查询中使用DISTINCT ON RESULTS可能是不确定的。换句话说,对于相同的表,查询可能返回不同的结果。
一个有趣的方面
DISTINCT ON以一种更干净的方式模拟了SQLite的坏行为。假设R有两个属性a和b:
是SQL中的非法语句。然而,它运行在SQLite上。它只是从a的相同值的组中的任何元组中取一个随机值b。在PostgreSQL中,此语句是非法的。相反,您必须使用DISTINCT ON并写入:
当您要访问在功能上依赖于GROUP BY属性的值时,DISTINCT ON在GROUP BY中非常有用。换句话说,如果您知道对于每组属性,它们的第三个属性的值总是相同的,那么对该组属性使用DISTINCT。否则,您将不得不进行联接以检索第三个属性。
rslzwgfq2#
第一个查询给出
dimension1
的非空值的数量,而第二个查询返回该列的不同值的数量。如果列包含重复项或空值,则这些数字显然不相等。中的单词
DISTINCT
没有任何意义,因为查询返回单行。也许你想要
它返回
dimension1
的非空值的个数。请注意,它不同于最后一个查询生成该列的所有不同值(是否为空)的数量。
ryoqjall3#
通过直观的例子来学习和理解所发生的事情。
下面是在PostgreSQL上执行的一些SQL代码:
pgky5nke4#
以下是一个更直接的摘要,可能对谷歌用户有用,它回答了标题,但没有回答全文的复杂之处:
SELECT DISTINCT
返回
col1
、col2
和col3
,并忽略所有col1、col2、col3都相同的行。例如,您可能会得到如下结果:因为这两行由于
4
而不相同。但你永远得不到:因为
1 2 3
出现两次,并且两行完全相同。这就是DISTINCT
所阻止的。GROUP BY
:SELECT DISTINCT
基本上是GROUP BY
的子集,其中不能使用聚合函数:Is there any difference between GROUP BY and DISTINCTSELECT DISTINCT ON
可用性:PostgreSQL扩展,WONTFIXED by SQLite
行为:与
DISTINCT
不同,DISTINCT ON
允许您将你想要独一无二的东西
从你想要退还的东西
例如:
返回
col2
和col3
,不返回具有相同col1
的任何两行。例如:无法发生,因为我们在
col1
上有两次1
。以及例如:
将防止任何重复的(col1、col2)元组,例如,您可以获得:
因为它具有不同的(1,2)和(1,4)元组,但没有:
如果(1,2)出现两次,则这两次中只能出现一次。
我们可以使用
ORDER BY
唯一地确定将选择哪一行可能的行,这保证了第一个匹配,例如:将确保在以下方面:
只有
1 2 4
会被选中,因为它首先出现在我们的DESC
排序中。VS
GROUP BY
:DISTINCT ON
不是GROUP BY
的子集,因为它允许您访问GROUP BY
中不存在的额外行,这在GROUP BY
中通常是不允许的,除非:在postgres(unique not null is a TODO for them)中按主键分组
或者如果在SQLite/MySQL中允许作为ISO扩展
这使得
DISTINCT ON
对于实现“查找达到某些列的最大/最小值的整行”的常见用例非常有用:Is there any difference between GROUP BY and DISTINCTRANK
和ROW_NUMBER
窗口函数这些基本可以用作
DISTINCT ON
的超集,并在SQLite 3.34和PostgreSQL 14.3中进行了实现测试。我强烈建议也研究一下它们,例如:如何从一列中选择不同的列并获得其他列?ikfrs5lh5#
尝试
DISTINCT ON似乎与GROUP BY同义。