如何从 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:
第一个 Window(按日期排序):
| Date | Amount | Product_ID | Rank |
|------------------|--------------|------------------|------------|
| 2016-01-10 | 7000 | A | 1 |
第二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 |
第三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 |
第四个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 函数来完成此操作的方法。这是一个带有显式 JOIN
和 GROUP 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 操作来计算排名。然而,即使那样也可能很棘手。
有没有办法从 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:
第一个 Window(按日期排序):
| Date | Amount | Product_ID | Rank | |------------------|--------------|------------------|------------| | 2016-01-10 | 7000 | A | 1 |
第二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 |
第三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 |
第四个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 函数来完成此操作的方法。这是一个带有显式 JOIN
和 GROUP 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 操作来计算排名。然而,即使那样也可能很棘手。