先前排名为零时如何分配排名(第 2 部分)

Posted

技术标签:

【中文标题】先前排名为零时如何分配排名(第 2 部分)【英文标题】:How to distribute ranks when prior rank is zero (part 2) 【发布时间】:2020-09-10 18:36:14 【问题描述】:

这是对我之前的问题How to distribute values when prior rank is zero 的扩展。该解决方案适用于 postgres 环境,但现在我需要复制到 databricks 环境 (spark sql)。

问题和以前一样,但现在尝试确定如何将这个postgres查询转换为spark sql。基本上,如果数据中有差距(即按位置和 geo3 分组时没有 micro_geo),它会汇总分配量。对于所有位置和 zip3 组,“估算分配”将等于 1。

这是 postgres 查询,效果很好:

    select location_code, geo3, distance_group, has_micro_geo, imputed_allocation from 
        (
        select ia.*,
               (case when has_micro_geo > 0
                     then sum(allocation) over (partition by location_code, geo3, grp)
                     else 0
                end) as imputed_allocation
        from (select s.*,
                     count(*) filter (where has_micro_geo <> 0) over (partition by location_code, geo3 order by distance_group desc) as grp
              from staging_groups s
             ) ia
        )z

但它不能很好地翻译并在数据块中产生此错误:

    Error in SQL statement: ParseException: 
    mismatched input 'from' expecting <EOF>(line 1, pos 78)

    == SQL ==
    select location_code, geo3, distance_group, has_micro_geo, imputed_allocation from 
    ------------------------------------------------------------------------------^^^
        (
        select ia.*,
               (case when has_micro_geo > 0
                     then sum(allocation) over (partition by location_code, geo3, grp)
                     else 0
                end) as imputed_allocation
        from (select s.*,
                     count(*) filter (where has_micro_geo <> 0) over (partition by location_code, geo3 order by distance_group desc) as grp
              from staging_groups s
             ) ia
        )z

或者至少,如何转换这个创建“grp”的内部查询的一部分,然后其余的可能会起作用。我一直在尝试用其他东西替换这个过滤器逻辑,但尝试并没有达到预期的效果。

    select s.*,
    count(*) filter (where has_micro_geo <> 0) over (partition by location_code, geo3 order by distance_group desc) as grp
    from staging_groups s
    

这是一个带有数据 https://www.db-fiddle.com/f/wisvDZJL9BkWxNFkfLXdEu/0 的 db-fiddle,当前设置为 postgres,但我需要再次在 spark sql 环境中运行它。我已尝试将其分解并创建不同的表格,但我的小组无法按预期工作。

这是一个更好地可视化输出的图像:

【问题讨论】:

【参考方案1】:

你需要重写这个子查询:

select s.*,
    count(*) filter (where has_micro_geo <> 0) over (partition by location_code, geo3 order by distance_group desc) as grp
from staging_groups s

虽然窗口和聚合函数的filter() 子句是标准SQL,但目前很少有数据库支持它。相反,考虑一个条件窗口sum(),它会产生相同的结果:

select s.*,
    sum(case when has_micro_geo <> 0 then 1 else 0 end) over (partition by location_code, geo3 order by distance_group desc) as grp
from staging_groups s

我认为查询的其余部分应该在 Spark SQL 中运行良好。

【讨论】:

【参考方案2】:

由于has_micro_geo 已经是 0/1 标志,您可以将计数(过滤器)重写为

sum(has_micro_geo)
over (partition by location_code, geo3
      order by distance_group desc
      rows unbounded preceding) as grp

添加rows unbounded preceding 以避免默认的range unbounded preceding 可能会降低性能。

顺便说一句,我已经在我对 Gordon 对您先前问题的解决方案的评论中写道:-)

【讨论】:

以上是关于先前排名为零时如何分配排名(第 2 部分)的主要内容,如果未能解决你的问题,请参考以下文章

根据排名为学生分配主题

为锦标赛系统分配奖品

在 mySQL 和 VIEW 创建中分配排名

rank 和 unrank 组合将 k 个球分配到 n 个不同容量的 bin 中

MySQL 分配在更新查询中仅匹配一次

Mysql分组排名