SQL中的Distinct关键字会拖慢性能吗?

17

我收到了一个使用distinct关键字的SQL查询。当我尝试运行这个查询时,连接两个拥有数十万条记录的表并实际返回结果至少需要一分钟。

然后我去掉了distinct,这次查询在0.2秒内返回了结果。这个distinct关键字真的会使情况变得那么糟糕吗?

以下是查询:

SELECT DISTINCT
    c.username, o.orderno, o.totalcredits, o.totalrefunds,
    o.recstatus, o.reason 
FROM management.contacts c 
    JOIN management.orders o ON (c.custID = o.custID)
WHERE o.recDate > to_date('2010-01-01', 'YYYY/MM/DD')

3
你能否发布实际的查询?根据我的经验,添加"distinct"关键词通常是为了修正编写不良查询的一种hack方式。 - Joe Stefanelli
坦白说,我实际上不理解这个distinct语句的目的,而且编写它的人早已离开了... - MxLDevs
@Keikoku:查询使用distinct和不使用distinct返回的行数是否不同? - Joe Stefanelli
我目前正在让它运行。它已经超过了100,000,但仍在继续。无论如何,在选择了这么多列时使用DISTINCT的目的是什么?我无法想象那会有什么用处。 - MxLDevs
@Keikoyu:我也有同样的怀疑。看起来 o.orderno 可能足以使每一行都唯一,但我显然不知道你的数据。 - Joe Stefanelli
我刚刚问了一下周围的人,有些人说可能会出现重复信息,但是当我加入所有这些列时,实际上并不会出现。如果使用distinct的唯一原因是返回唯一结果,那么具有两个或三个列(日期、订单号和名称)的高度不可能出现重复数据应该足够了。事实上,也许我应该只加入看起来像索引号的列...感谢您的帮助。 - MxLDevs
4个回答

19

是的,使用DISTINCT有时会导致结果被排序(根据一条评论)。对数百条记录进行排序需要时间。

尝试使用GROUP BY所有列,它有时会导致查询优化器选择更高效的算法(至少我在Oracle中注意到了显著的性能提升)。


3
小小的注释:通过对结果进行排序并不一定能够完成去重。数据库可以自由选择任何它认为合适的策略来计算唯一行。Oracle可以根据需要使用索引或哈希来确定应该唯一的列。这两种方法都不会导致排序步骤。但是如果涉及到排序,则可能会非常昂贵。 - user330315
@a_horse_with_no_name,(即使较小的侧面也不是侧面注释):哈希也是一个索引。 因此,虽然不需要排序,但需要构建索引。 - Unreason
2
@Unreason: 但是哈希不会对结果进行排序。这就是我想要指出的问题。而且索引扫描也不一定有序。 - user330315

13

对我来说,“distinct”总会引起警报——它通常表示糟糕的表设计或不确定自己的开发人员。虽然它用于去除重复行,但如果联接正确,则很少需要它。而且,使用它是有很大代价的。

订单表的主键是什么?假设是“orderno”,那么应该足以保证没有重复项。如果是其他内容,则可能需要在查询中多做一些工作,但你应该把删除那些“distinct”作为一个目标! ;-)

此外,当您检查返回的大量行时,包装整个查询在“select count(*) from ( )”中通常可以更快地运行。只是在测试时。 ;-)

最后,请确保已经在订单表上建立了custID索引(也可能是recDate)。


1
是的,我找到了使用distinct的原因,那是因为他们想要将所有订单和客户连接在一起,但最终出现了重复订单,因为某些原因单个订单包含不相关的不同信息而生成了多个记录。但计数提示很好,似乎有600万条记录。 - MxLDevs

4
DISTINCT的目的是从所有选定的列的结果集中修剪重复记录。
如果任何选定的列在连接后是唯一的,您可以删除DISTINCT。
如果您不知道,但您知道所选列的值组合是唯一的,则可以删除DISTINCT。
实际上,通常情况下,通过正确设计的数据库,您很少需要DISTINCT,在那些情况下,您需要它是显然的。然而,关系型数据库管理系统不能留给机会,必须实际建立一个索引结构来建立它。
通常,当人们不确定JOIN和表之间的关系时,您会在各个地方找到DISTINCT。
此外,在谈论纯关系数据库的类中,结果应该是一个适当的集合(没有重复元素=记录),为了理论上的正确性,人们经常将其插入以保证此属性。有时,这会渗入到生产系统中。

常规做法是使用DISTINCT吗?我快速浏览了一下同一个人编写的其他查询,每个查询都使用了DISTINCT,即使他在查询一个为每个条目添加唯一编号的列。在这种情况下,该编号并不需要,但也许我应该将其加入以确保唯一性。 - MxLDevs
2
不应该成为常规做法。它会降低性能(除非查询规划器可以确定它是多余的;我不知道Oracle处理得如何)。您应该从连接的基数、列的唯一性、应用的条件以及期望的结果中了解是否需要它。 - Unreason

0
你可以尝试像这样进行分组:
  SELECT c.username, 
         o.orderno, 
         o.totalcredits, 
         o.totalrefunds,
         o.recstatus, 
         o.reason
    FROM management.contacts c,
         management.orders o
   WHERE c.custID = o.custID
     AND o.recDate > to_date('2010-01-01', 'YYYY-MM-DD')
GROUP BY c.username, 
         o.orderno, 
         o.totalcredits, 
         o.totalrefunds,
         o.recstatus, 
         o.reason 

同时验证一下你是否在 o.recDate 上建立了索引


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接