首页 > 解决方案 > 在非规范化表中分组

问题描述

我们有一个包含非规范化数据的表,其中存储事件,其中包括以下字段:ProductId | 产品名称 | 子类别 ID

它们直接相互依赖,因此对于特定的 productid,Productname 和 subcategoryid 可能只有一个值。由于我们存储事件,因此我们有许多行将包含与上述三个字段相关的相同信息。

我们现在正在设计一个查询来获取唯一的 productid、productname 和 subcategoryid。这些查询将返回相同的实体:

select ProductId from VisitEvents
group by ProductId

作为这个:

select productid, productname, subcategoryid from visitevents
group by productid, productname, subcategoryid

然而,后者要慢几个数量级。除了第一个查询中分组的内容之外,有没有办法投影其他字段?

标签: sql-serverperformancedenormalization

解决方案


我对速度问题的潜在原因发表了评论(主要可能是由于排序所需的读取和处理增加)。

但是,我对您的问题的主要理解是

  • 您的数据实际上只是按 productid 分组,但由于它是非规范化的,因此它具有与 productid 直接相关的其他列
  • 因为您要对所有三个字段进行分组,所以速度很慢 - 您希望通过仅对 productid 进行分组来加快速度

答案 1:索引

如果您在这些列上放置索引,它将已经被预先排序。但是,我的猜测是您的索引已经非常密集,因此更多的索引可能会导致问题。如果没有,请尝试在所有 3 个字段上添加索引,或者如果某些字段太大(例如,nvarchar(4000)),则在其余字段上添加索引,并“包含”大字段。

调整查询

对于这些,我们接受我们必须在查询中进行排序。问题是如何减少处理和/或读取。

如果没有统计数据和/或执行计划,这可能很难 - 但这里有一些建议。

我相信下面的第一种方法不会真正起作用,但值得一试。

select productid, MAX(productname) as productname, MAX(subcategoryid) as subcategoryid
from visitevents
group by productid

我希望你看到我正在尝试做的事情 - 将分组减少到一个字段。然而,由于它仍然需要进行排序以获得最大值,它可能仍然很慢(尽管它们没有被排序在一起,它可能会快一点)。

另一种方法是对 productid 进行排序/组并获取任何相应的值(您不在乎哪个)。一种方法是

; WITH cte AS 
  (select productid, productname, subcategoryid,
   ROW_NUMBER() OVER (PARTITION BY productid ORDER BY productid) AS rn
   from visitevents
  )
SELECT productid, productname, subcategoryid
FROM cte
WHERE cte.rn = 1

对于上述内容,它获取所有数据,按 productid 对其进行排序,然后(有点随机)为该组中的每一行分配行号。然后它只获取所有第一行。

它很丑。它有点滥用窗口函数(PARTITION BY productid ORDER BY productid???)根据定义,这个 ORDER BY 什么都不做,但包含在内是因为您必须在 ROW_NUMBER 中有一个 ORDER BY。

什么都不做是你想要的——不想把 CPU 周期花在额外的种类上。所以希望它可能会有所帮助。


推荐阅读