在非规范化表中分组依据

问题描述

我们有一个包含非规范化数据的表,该表存储事件以及以下字段: 产品编号|产品名称| SubcategoryId

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

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

select ProductId from VisitEvents
group by ProductId

与此一样:

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

但是,后者要慢几个数量级。 除了第一个查询中要分组的字段之外,还有什么方法可以投射其他字段?

解决方法

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

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

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

答案1:编制索引

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

调整查询

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

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

我认为下面的第一种方法不会确实有效,但值得尝试。

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

希望您能看到我正在尝试做的-将分组依据减少到一个字段。但是,由于仍需要进行排序以获取最大值,因此它可能仍然很慢(尽管由于未将它们排序在一起,所以可能会更快一些)。

一种替代方法是获取productid的排序/组,并获取相应值的 any (您不在乎哪个)。一种方法是

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

对于上面的方法,它获取所有数据,按乘积id对其进行排序,然后(随机地)为该组中的每一行分配行号。然后,它仅获取所有第一行。

这很丑。这种滥用窗口函数(PARTITION BY productid ORDER BY productid ???)的定义是,此ORDER BY不会执行任何操作,但会包含在内,因为您必须在ROW_NUMBER内有一个ORDER BY。

什么也不做是您想要的-不想在其他种类上花费CPU周期。因此,希望它会有所帮助。