SQL公平地选择随机组合的行

Mat*_*ker 5 sql-server

这是我的问题:我在数据库中有一组表,其中填充了来自包含产品信息的客户端的数据。除了基本的产品信息,还有关于制造商的信息,以及这些产品的类别(一个产品可以是一个或多个类别)。这些类别然后被称为“产品类别”,这些产品在哪些商店可用。这些表每周根据客户的提要更新一次。

由于出于我们的目的,某些产品类别与我们的目的相同或密切相关,因此还有另一种级别的类别称为“一般类别”,一般类别可以包含一个或多个产品类别。

对于这些表的范围,这里有一些粗略的数字:

Data Tables:
Products:           475,000
Manufacturers:      1300
Stores:             150
General Categories: 245
Product Categories: 500

Mapping Tables:
Product Category -> Product: 655,000
Stores -> Products:          50,000,000
Run Code Online (Sandbox Code Playgroud)

现在,对于实际问题:作为我们软件的一部分,我们需要选择n随机产品,给定商店和一般类别。但是,我们还需要确保制造商的良好组合,因为在某些类别中,单个制造商主导了结果,随机选择行会导致结果强烈偏向该制造商。

当前采用的解决方案适用于大多数情况,包括选择与商店和类别标准匹配的所有行,按制造商对它们进行分区,并在其分区中包含它们的行号,然后从中选择行号为该制造商小于n,并用于ROWCOUNT钳制返回到 的总行数n

这个查询看起来像这样:

SET ROWCOUNT 6
select p.Id, GeneralCategory_Id, Product_Id, ISNULL(m.DisplayName, m.Name) AS Vendor, 
       MSRP, MemberPrice, FamilyImageName 
    from (select p.Id, gc.Id GeneralCategory_Id, 
            p.Id Product_Id, ctp.Store_id, Manufacturer_id, 
            ROW_NUMBER() OVER (PARTITION BY Manufacturer_id ORDER BY NEWID()) AS 'VendorOrder', 
            MSRP, MemberPrice, FamilyImageName
            from GeneralCategory gc
                inner join GeneralCategoriesToProductCategories gctpc ON gc.Id=gctpc.GeneralCategory_Id
                inner join ProductCategoryToProduct pctp on gctpc.ProductCategory_Id = pctp.ProductCategory_Id
                inner join Product p on p.Id = pctp.Product_Id
                inner join StoreToProduct ctp on p.Id = ctp.Product_id
                where gc.Id = @GeneralCategory and ctp.Store_id=@StoreId and p.Active=1 and p.MemberPrice >0) p 
    inner join Manufacturer m on m.Id = p.Manufacturer_id
    where VendorOrder <=6
order by NEWID()

SET ROWCOUNT 0
Run Code Online (Sandbox Code Playgroud)

(我试图对其进行某种格式化以使其更干净,但我认为这并没有真正的帮助)

使用执行计划运行此查询表明,对于这些表中的大多数,它正在执行聚集索引查找。有两个操作占用了大约 90% 的时间:

  1. StoreToProduct 上的索引搜索(非聚集):17%。该表仅包含商店的密钥和产品的密钥。NHibernate 似乎决定在制作这张表时不制作复合键,但与其他搜索相比,我此时并不关心这一点......
  2. 产品的聚集索引搜索:69%。我真的不知道如何让这个更高效。

在没有很多产品的类别上,性能是可以接受的(<50ms),但是较大的类别可能需要几百毫秒,最大的类别需要 3 秒(大约有 17 万个产品)。

从这一点来看,我似乎有两种方法:

  1. 以某种方式优化现有查询和表索引以减少查询时间。由于几乎每个昂贵的操作都已经是聚集索引扫描,我不知道在那里可以做什么。内部查询可以调整为不返回该类别的所有可能行,但我不确定如何执行此操作并保持要求(随机产品,制造商的良好组合)
  2. 在执行每周一次的导入时,出于此查询的目的,对这些数据进行非规范化。但是,我不确定如何执行此操作并保持要求。

有没有人对这些项目中的任何一个有任何意见?

小智 1

计划 C:对于每个商店和一般类别,使用您当前的方法随机选择 N 个产品,并将其临时存储在某个地方。每次有人获取您预取的随机组之一时,请重新填充该商店/类别。如果不需要是最新的,那么就不需要让客户端等待您获取最新数据。