【问题标题】:How to use rank() over PARTITION BY in Mysql如何在 Mysql 中使用 rank() 而不是 PARTITION BY
【发布时间】:2019-08-20 18:48:13
【问题描述】:

假设有三种材质类型,例如 ('COTTON', 'LEATHER', 'SILK'),我想获取具有这三种材质类型的dress_id。我也想给他们排名。

有人可以逐步解释如何做到这一点吗? 我列举了几个例子,但对我来说似乎都不清楚。

The output should look something like

DRESS_ID   MATERIAL LAST_UPDATED_DATE RANK
111        COTTON   2019-08-29         1
111        SILK     2019-08-30         2
111        LEATHER  2019-08-31         3
222        COTTON   2019-08-29         1
222        SILK     2019-08-30         2
222        LEATHER  2019-08-31         3
222        LEATHER  2019-09-02         4

执行此查询时,我在 MYSQL 工作台中遇到错误。 错误代码:1305。FUNCTION rank 不存在。

SELECT dress_id,
       rank() over(PARTITION BY dress_id, material ORDER by LAST_UPDATED_DATE asc) as rank
FROM dress_types;

【问题讨论】:

  • 您需要 MySQL 8+ 才能使用窗口函数。您似乎使用的是旧版本。
  • SELECT VERSION() 至少 MySQL 8,低于 8 的 MySQL 不支持 windows 功能..
  • @GordonLinoff - 是的,我知道我使用的版本是旧的(即 5.6.40-log):-) 但是如何在我的版本中实现这个逻辑?

标签: mysql sql window-functions dense-rank


【解决方案1】:

在早期版本的 MySQL 中,您可以使用变量或相关子查询。

因为每件衣服只有少量材料,所以关联子查询是合理的,尤其是在具有正确索引的情况下。代码如下:

SELECT d.dress_id, d.material,
       (SELECT COUNT(*)
        FROM dress_types d2
        WHERE d2.dress_id = d.dress_id AND
              d2.last_updated_date <= d.last_updated_date
       ) as rank
FROM dress_types d;

请注意,这会根据您的 数据 而不是查询来实现逻辑。相应的查询是:

SELECT dress_id,
       rank() over (PARTITION BY dress_id ORDER by LAST_UPDATED_DATE asc) as rank
FROM dress_types;

您想要的索引位于dress_types(dress_id, last_updated_date)

实际上,只要没有重复(按日期),这些都是相同的。如果有重复,逻辑可能会有所不同。

【讨论】:

    【解决方案2】:

    对于 MySQL 8.0 之前的版本,您必须使用变量来模拟排名:

    SET @rownum := 0;
    SET @group_number := 0;
    SELECT dress_id, material, last_updated_date, rank FROM (
    SELECT @rownum := case 
      when @group_number = dress_id then @rownum + 1 
      else 1
    end AS rank, dress_id, material, last_updated_date,
    @group_number := dress_id  
    FROM dress_types 
    ORDER BY 
      dress_id, 
      FIELD(material, 'COTTON', 'SILK', 'LEATHER'), 
      last_updated_date 
    ) t
    

    请参阅demo
    结果:

    | dress_id | material | last_updated_date   | rank |
    | -------- | -------- | ------------------- | ---- |
    | 111      | COTTON   | 2019-08-29 00:00:00 | 1    |
    | 111      | SILK     | 2019-08-30 00:00:00 | 2    |
    | 111      | LEATHER  | 2019-08-31 00:00:00 | 3    |
    | 222      | COTTON   | 2019-08-29 00:00:00 | 1    |
    | 222      | SILK     | 2019-08-30 00:00:00 | 2    |
    | 222      | LEATHER  | 2019-08-31 00:00:00 | 3    |
    | 222      | LEATHER  | 2019-09-02 00:00:00 | 4    |
    

    【讨论】:

      【解决方案3】:
      SELECT T.*,
        CASE WHEN @prev_dress_id != T.dress_id THEN @rank:=1
             ELSE @rank:=@rank+1 
        END as rank,
        @prev_dress_id := T.dress_id as set_prev_dress_id
      FROM 
        (SELECT dress_id,material,last_updated_date
        FROM dress_types T1
        WHERE EXISTS (SELECT 1 FROM dress_types E1 WHERE E1.dress_id = T1.dress_ID AND E1.material = 'COTTON')
          AND EXISTS (SELECT 1 FROM dress_types E2 WHERE E2.dress_id = T1.dress_ID AND E2.material = 'SILK')
          AND EXISTS (SELECT 1 FROM dress_types E3 WHERE E3.dress_id = T1.dress_ID AND E3.material = 'LEATHER')
        ORDER BY dress_id asc,last_updated_date asc
        )T,(SELECT @prev_dress_id:=-1)V
      

      内部选择选择所有 3 种材料都存在并按dress_id、last_updated_date 排序的连衣裙。 外部将它与可在每行末尾设置的 prev_dress_id 变量连接起来。基于@prev_dress_id != 或= T.dress_id 计算排名的case 语句中的逻辑。 sqlfiddle

      【讨论】:

        【解决方案4】:
        SELECT dress_id
             , material
             , LAST_UPDATED_DATE
           rank() over(PARTITION BY dress_id ORDER by LAST_UPDATED_DATE asc) as rank
        FROM dress_types
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-06-22
          • 2012-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-29
          • 1970-01-01
          相关资源
          最近更新 更多