【问题标题】:如何通过排名选择每组前 10 名
【发布时间】:2021-12-05 17:35:24
【问题描述】:

我想从CountryProductType 中选出过去 7 天内每个PriceGrouping 中观看次数最多的前 3 个Products

例如,我想要:

  • 日本 500 美元以下的前 3 名蓝色小工具
  • 日本 500 美元以上的前 3 名蓝色小工具
  • 日本 500 美元以下的前 3 名红色小工具
  • 日本 500 美元以上的前 3 名红色小工具
  • 俄罗斯 500 美元以下的前 3 名蓝色小工具

我对每个四舍五入的 LatLng 进行排名,以便按位置对 Products 进行分组,从而为 Google 地图数据层创建一个 geoJSON。每个圆形的LatLng 都有一个带有信息窗口的标记,在该位置显示一个或多个Products

我想展示每个Product 与每个Country 中相同ProductType 的其他人的比较。

例如,Marker1 显示:

Product      Type      Views       Comparison
-----------------------------------------------------------------
ABC          Type1     650         No.1 of Blue Widgets in Russia
DEF          Type2     341         No.6 of Red Widgets in Russia

Marker2 显示:

Product      Type      Views       Comparison
-----------------------------------------------------------------
XYZ          Type1     543         No.2 of Blue Widgets in Russia
RST          Type2     943         No.1 of Red Widgets in Russia

PRODUCTS表:

ProductID int
ProductType varchar(30)
Price DECIMAL (10,0)

VIEWS表:

每次在网站上查看产品时,都会在 Views 表中创建一条记录。

ProductID int
ViewDate datetime

SQL:

WITH Weights AS
(
    SELECT 
        s.ProductID,
        p.Country,
        p.Product_Type,
        ROUND(p.Latitude, 3) AS Latitude,
        ROUND(p.Longitude, 3) AS Longitude,
        CASE 
            WHEN p.Price > 0 AND p.Price < 500 THEN 1
            WHEN p.Price >= 500  THEN 2 
        END AS PriceGroup,
        COUNT(s.ProductID) AS Weight
    FROM 
        Views s
    JOIN 
        Products p ON s.ProductID = p.ProductID
    WHERE 
        s.ViewDate >= CAST(GETDATE()-7 AS date) 
        AND p.Latitude IS NOT NULL 
        AND p.Longitude IS NOT NULL 
    GROUP BY 
        s.ProductID, p.Country, p.ProductType,
        CASE WHEN p.Price > 0 AND p.Price < 500 THEN 1
             WHEN p.Price >= 500 THEN 2 END,
        ROUND(p.Latitude, 3),
        ROUND(p.Longitude, 3)
)

此 CTE 让我了解过去 7 天内查看过的每个产品的查看次数。

然后我尝试获取每个国家、ProductType、PriceGroup 的 TOP 3 产品:

SELECT TOP 3 WITH TIES
ProductID,
Country,
ProductType,
PriceGroup,
Latitude,
Longitude,
Weight,
DENSE_RANK() OVER(ORDER BY Latitude,Longitude) As Rank,
DENSE_RANK() OVER(PARTITION BY Country,ProductType ORDER BY Weight DESC) As Rank2
FROM Weights
ORDER BY ROW_NUMBER() OVER (PARTITION BY Country,ProductType,PriceGroup ORDER BY Weight DESC)

问题

  1. 每个国家/地区的每个 ProductType 和 PriceGroup 仅返回 1 个产品,而不是每个返回 3 个
  2. Rank 没有返回不间断的数字序列 - 即它返回 1,2,4,5,6,8,10,11
  3. Rank2 没有返回从 1 到 3 的不间断序列 - 即它返回 5,4,1 - 我实际上无法弄清楚它在任何列上的排名情况

【问题讨论】:

    标签: sql-server group-by greatest-n-per-group row-number dense-rank


    【解决方案1】:

    如果您只是将ROW_NUMBER 放在 CTE 中然后过滤它,这可能会更容易,而不是使用 TOP WITH TIES

    注意我是如何预先聚合Views 并在APPLY 中应用计算的,这样可以避免重复任何代码。

    WITH Weights AS
    (
        SELECT 
            p.ProductID,
            p.Country,
            p.Product_Type,
            v.Latitude,
            v.Longitude,
            v.PriceGroup,
            s.Weight,
            ROW_NUMBER() OVER (PARTITION BY p.Country, p.ProductType, v.PriceGroup
                              ORDER BY s.Weight DESC) AS Rank
        FROM Products p
        CROSS APPLY (
            SELECT COUNT(*) AS Weight
            FROM Views s
            WHERE s.ViewDate >= CAST(GETDATE()-7 AS date)
              AND s.ProductID = p.ProductID
            GROUP BY s.ProductID
        ) s
        CROSS APPLY (VALUES(
            CASE 
                WHEN p.Price > 0 AND p.Price < 500 THEN 1
                WHEN p.Price >= 500  THEN 2 
            END,
            ROUND(p.Latitude, 3),
            ROUND(p.Longitude, 3)
        )) AS v(PriceGroup, Latitude, Longitude)
        WHERE p.Latitude IS NOT NULL 
          AND p.Longitude IS NOT NULL
    )
    SELECT
      ProductID,
      Country,
      ProductType,
      PriceGroup,
      Latitude,
      Longitude,
      Weight,
      Rank
    FROM Weights
    WHERE Rank <= 3
    ORDER BY
      Rank,
      Country,
      ProductType;
    

    【讨论】:

    • 谢谢。 CROSS APPLY VALUES 上缺少两个括号,但我也收到“Invalid column name ProductID”错误,我不明白为什么会这样
    • 这几乎是正确的 - 查看返回的数据,它输出的产品没有权重/浏览量以及那些有的产品。我只想知道最近7天浏览过的产品,然后排名
    • 好的,现在应该都修好了
    • 谢谢!我非常感谢您的时间和精力。我现在只需要添加一个 DENSE_RANK,这样我就可以按 LatLng 对产品进行分组。我想我可以做到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多