如何从 Window 顺序以外的另一列计算 RANK?

How to calculate the RANK from another column than the Window order?

有没有办法从 Window 的排序中指定的列以外的另一列计算 "Rank"?

为了更清楚地解释我想做什么,我将使用以下示例:

数据示例:

|       Date       |    Amount    |    Product_ID    |
|------------------|--------------|------------------|
|    2016-01-10    |     7000     |         A        |
|    2016-02-01    |     1000     |         A        |
|    2016-01-08    |     10000    |         B        |
|    2016-02-10    |     2000     |         B        |
|    2016-03-05    |     3000     |         A        |
|    2016-04-01    |     10000    |         A        |
|    2016-03-20    |     4000     |         B        |
|    2016-05-01    |     8500     |         B        |
|    2016-05-15    |     2000     |         A        |
|------------------|--------------|------------------|

问题

所以在这个例子中,我想首先将 Window 按 "Product_ID" 划分并按 "Date" 排序,但计算出的排名应该在 "Amount" ] 列而不是有序列 "Date":

|       Date       |    Amount    |    Product_ID    |    Rank    |
|------------------|--------------|------------------|------------|
|    2016-01-10    |     7000     |         A        |     1      |
|    2016-02-01    |     1000     |         A        |     1      |
|    2016-03-05    |     3000     |         A        |     2      |
|    2016-04-01    |     10000    |         A        |     4      |
|    2016-05-15    |     2000     |         A        |     2      |
|    2016-01-08    |     10000    |         B        |     1      |
|    2016-02-10    |     2000     |         B        |     1      |
|    2016-03-20    |     4000     |         B        |     2      |
|    2016-05-01    |     8500     |         B        |     3      |
|------------------|--------------|------------------|------------|

我想按 "Date" 订购 Window,这样我只计算过去日期的 "Amount Rank"。

说明

为了解释得更清楚,在 Product_ID 的分区上 A:

  1. 第一个 Window(按日期排序):

    |       Date       |    Amount    |    Product_ID    |    Rank    |
    |------------------|--------------|------------------|------------|
    |    2016-01-10    |     7000     |         A        |     1      |
    
  2. 第二Window:这里由于第二行的Amount是1000不如7000(Window第一行的amount按日期排序),所以"Rank" 应该是 1。

    |       Date       |    Amount    |    Product_ID    |    Rank    |
    |------------------|--------------|------------------|------------|
    |    2016-01-10    |     7000     |         A        |     1      |
    |    2016-02-01    |     1000     |         A        |     1      |
    
  3. 第三Window:同上逻辑,得到"Rank"2因为3000在子组[7000,1000,3000][=21中排名第二=]

    |       Date       |    Amount    |    Product_ID    |    Rank    |
    |------------------|--------------|------------------|------------|
    |    2016-01-10    |     7000     |         A        |     1      |
    |    2016-02-01    |     1000     |         A        |     1      |
    |    2016-03-05    |     3000     |         A        |     2      |
    
  4. 第四个Window:同上逻辑

    |       Date       |    Amount    |    Product_ID    |    Rank    |
    |------------------|--------------|------------------|------------|
    |    2016-01-10    |     7000     |         A        |     1      |
    |    2016-02-01    |     1000     |         A        |     1      |
    |    2016-03-05    |     3000     |         A        |     2      |
    |    2016-05-15    |     2000     |         A        |     2      |
    

等等。

我试过的

我尝试了以下代码来获得我想要的,即按 Product_ID 分区,按日期排序 window 并获得排名:

    SELECT
          Date,
          Amount,
          Product_ID,
          RANK() OVER(PARTITION BY Product_ID ORDER BY Date) AS Rank
    FROM Data

这段代码给出了以下结果:

|       Date       |    Amount    |    Product_ID    |    Rank    |
|------------------|--------------|------------------|------------|
|    2016-01-10    |     7000     |         A        |     1      |
|    2016-02-01    |     1000     |         A        |     2      |
|    2016-03-05    |     3000     |         A        |     3      |
|    2016-04-01    |     10000    |         A        |     4      |
|    2016-05-15    |     2000     |         A        |     5      |
|    2016-01-08    |     10000    |         B        |     1      |
|    2016-02-10    |     2000     |         B        |     2      |
|    2016-03-20    |     4000     |         B        |     3      |
|    2016-05-01    |     8500     |         B        |     4      |
|------------------|--------------|------------------|------------|

我已经按金额尝试了相同的订单:

    SELECT
          Date,
          Amount,
          Product_ID,
          RANK() OVER(PARTITION BY Product_ID ORDER BY Amount) AS Rank
    FROM Data

这个新代码给了我以下结果:

|       Date       |    Amount    |    Product_ID    |    Rank    |
|------------------|--------------|------------------|------------|
|    2016-02-01    |     1000     |         A        |     1      |
|    2016-05-15    |     2000     |         A        |     2      |
|    2016-03-05    |     3000     |         A        |     3      |
|    2016-01-10    |     7000     |         A        |     4      |
|    2016-04-01    |     10000    |         A        |     5      |
|    2016-02-10    |     2000     |         B        |     1      |
|    2016-03-20    |     4000     |         B        |     2      |
|    2016-05-01    |     8500     |         B        |     3      |
|    2016-01-08    |     10000    |         B        |     4      |
|------------------|--------------|------------------|------------|

诺塔贝内斯

N.B.1:我试过在 Spark SQL 上做,所以 SQL 是基本的。使用 Scala 或 pySpark 的任何答案也是可以接受的。

N.B.2:这是我在 Stack Overflow post 上的第一个

非常感谢您的回答和理解。

非常有趣的问题。您似乎想要按日期计算金额的累积排名。

我想不出使用 window 函数来完成此操作的方法。这是一个带有显式 JOINGROUP BY 的方法:

SELECT d.Product_Id, d.Date, d.Amount,
       SUM(CASE WHEN d2.Amount < d.Amount THEN 1 ELSE 0 END) + 1 as rank
FROM Data d JOIN
     Data d2
     ON d2.Product_Id = d.Product_Id AND
        d2.Date <= d.Date
GROUP BY d.Product_Id, d.Date, d.Amount;

当然,性能不如 window 函数方法。

一种适用于某些数据库的方法是将金额累加到字符串或数组中,然后使用 string/array 操作来计算排名。然而,即使那样也可能很棘手。