如何计算除Window顺序之外的其他列的RANK?

时间:2017-10-17 11:40:34

标签: sql apache-spark-sql window-functions

有没有办法从另一列计算“排名”而不是窗口排序中指定的那一列?

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

数据示例:

|       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        |
|------------------|--------------|------------------|

问题

所以在这个例子中,我希望,首先按“Product_ID”对Window进行分区,然后按“Date”排序,但是计算出的等级应该是“Amount”列,而不是有序列“Date”: / p>

|       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      |
|------------------|--------------|------------------|------------|

我想按“日期”订购窗口,以便我只计算过去日期的“金额排名”。

解释

更清楚地解释一下,在Product_ID A上的分区:

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

    |       Date       |    Amount    |    Product_ID    |    Rank    |
    |------------------|--------------|------------------|------------|
    |    2016-01-10    |     7000     |         A        |     1      |
    
  2. 第二个窗口:这里,由于第二行的数量是1000以下7000(按日期排序的窗口第一行的数量),“等级”应为1.

    |       Date       |    Amount    |    Product_ID    |    Rank    |
    |------------------|--------------|------------------|------------|
    |    2016-01-10    |     7000     |         A        |     1      |
    |    2016-02-01    |     1000     |         A        |     1      |
    
  3. 第三个窗口:使用与上面相同的逻辑,我们得到“Rank”2,因为3000在子组中排名第二[7000,1000,3000]

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

    |       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      |
    
  5. 等等。

    我尝试了什么

    我尝试了以下代码来获取我想要的内容,即按Product_ID进行分区,按日期排序窗口并获取Rank:

        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      |
    |------------------|--------------|------------------|------------|
    

    Nota Benes

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

    N.B.2:这是我关于Stack Overflow的第一篇文章

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

1 个答案:

答案 0 :(得分:0)

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

我不能轻易想到使用窗口函数的方法。以下是一种明确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;

当然,性能不如窗口函数方法那么好。

在某些数据库中可以使用的一种方法是将金额累积到字符串或数组中,然后使用字符串/数组操作来计算排名。但是,即使这样也可能很棘手。