【问题标题】:How can I avoid using a TSQL select distinct in this situation?在这种情况下如何避免使用 SQL select distinct?
【发布时间】:2020-10-30 05:25:35
【问题描述】:

我正在调整一个查询,该查询在数百万条记录上运行 select distinct。我对执行计划有点缺乏经验,但我对 distinct 的理解是,我们希望避免使用它,因为清除重复记录需要额外的开销。

有没有办法在不使用 distinct 的情况下重写下面的示例?

IF OBJECT_ID('TEMPDB..#ORDERS', 'U') IS NOT NULL
    DROP TABLE #ORDERS

IF OBJECT_ID('TEMPDB..#CUSTOMERS', 'U') IS NOT NULL
    DROP TABLE #CUSTOMERS

CREATE TABLE #ORDERS (OrderLineItemID INT IDENTITY(1, 1), OrderID INT, PRIMARY KEY (OrderLineItemID));

CREATE TABLE #CUSTOMERS (CustomerID INT, OrderLineItemID INT, PRIMARY KEY (OrderLineItemID));

INSERT INTO #ORDERS (OrderID)
VALUES (1), (1), (1), (2), (2), (2), (2), (3), (3), (3), (3), (3), (3), (3), (5), (5), (5), (5), (5), (5);

INSERT INTO #CUSTOMERS (OrderLineItemID, CustomerID)
SELECT OrderLineItemID, CASE 
        WHEN OrderLineItemID <= 3
            THEN 15
        ELSE 20
        END
FROM #ORDERS

查询结果需要拉取CustomerID和OrderID,但是每个订单里面都有一个单独的line item。下面拉出所有结果。

SELECT C.CustomerID, O.OrderID
FROM #CUSTOMERS C
JOIN #ORDERS O ON C.OrderLineItemID = O.OrderLineItemID

非明显结果:

CustomerID  OrderID
15  1
15  1
15  1
20  2
20  2
20  2
20  2
20  3
20  3
20  3
20  3
20  3
20  3
20  3
20  5
20  5
20  5
20  5
20  5
20  5

但如果我们在选择中添加 distinct,我们会得到想要的结果。

SELECT DISTINCT C.CustomerID, O.OrderID
FROM #CUSTOMERS C
JOIN #ORDERS O ON C.OrderLineItemID = O.OrderLineItemID

不同的结果:

CustomerID  OrderID
15  1
20  2
20  3
20  5

有没有更好的写法来提高效率?

【问题讨论】:

  • 一般来说,如果一个成熟的系统提供了一个内置的方法来做某事,你可能不会写一个更有效的解决方法。
  • 您的架构似乎是主要问题。一般来说,schema问题会导致tsql繁琐且效率低下。为什么我说“问题”?我不知道订单与不同客户相关联的现实情况 - 在您的情况下,订单中的每个项目都与客户相关。解决架构问题可能会解决许多问题。
  • 这只是一个例子。真实数据其实与订单无关。我把它放在一起来演示非不同连接查询将如何返回重复与不同。
  • 因此,如上所述,您的情况具有误导性,使 cmets 和建议的用处降低。但是一般的含义是 - 使用 DISTINCT 的需要通常是由模式问题或逻辑上有缺陷的查询引起的。 Oso 是正确的——你可能无法胜过内置函数。我还要指出,您的结果集没有排序,并且行顺序通常很重要。由于运行时因素,DISTINCT 通常会导致行显示为有序 - 但不能保证。
  • cmets 仍然适用,因为该示例的设置方式与我查询的源完全相同。我本可以让列标题更加模棱两可,如果这引起了混乱,我很抱歉。无论如何,在这种情况下,如果不对架构进行大修,就没有解决方法。将这些放在一起的开发人员无意进行增强。感谢您的建议。

标签: sql-server tsql duplicates distinct


【解决方案1】:

您可以使用窗口函数ROW_NUMBER() 来实现相同的结果。不确定,它是否会提供更好的性能。如有疑问,最好进行测试。

SELECT CustomerID, OrderID
FROM
(
SELECT C.CustomerID, O.OrderID, ROW_NUMBER() OVER(PARTITION BY C.CustomerID, O.OrderID ORDER BY (SELECT 1)) AS RNK
FROM #CUSTOMERS C
JOIN #ORDERS O ON C.OrderLineItemID = O.OrderLineItemID) as t
WHERE rnk = 1

【讨论】:

  • 这不太可能是一种改进。 DISTINCT 可以使用排序策略或散列策略。这会强制执行排序策略。
猜你喜欢
  • 2011-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-09
  • 2015-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多