问题描述
我们有一个包含非规范化数据的表,该表存储事件以及以下字段: 产品编号|产品名称| 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周期。因此,希望它会有所帮助。