如果行数少于N,则为每个类别选择前N个而不进行排序

时间:2019-02-22 06:17:04

标签: sql apache-spark greatest-n-per-group

给出下表,问题是从每个C1中查找例如前N个C2。

C1 C2 
1  1
1  2
1  3
1  4
1  ...
2  1
2  2
2  3
2  4
2  ...
....

因此,如果N = 3,则结果为

C1 C2 
1  1
1  2
1  3
2  1
2  2
2  3
....

建议的解决方案使用窗口功能并通过

进行分区

例如,

SELECT rs.Field1,rs.Field2 
FROM (
    SELECT Field1,Field2, Rank() 
      over (Partition BY Section
            ORDER BY RankCriteria DESC ) AS Rank
    FROM table
) rs WHERE Rank <= 3

我想它的作用是排序,然后选择前N个。

但是,如果某些类别中的N个元素较少,则我们可以获得无排序的前N个,因为前N个必须包括该类别中的所有元素。

上面的查询使用Rank()。我的问题适用于其他窗口函数,例如row_num()或density_rank()。

有没有办法忽略案件的排序?

我也不知道底层引擎是否可以优化这种情况:排序之前内部分区/顺序是否考虑了外部where约束。

使用partition + order + where是从每个类别中获取前N个元素的方法。如果每个类别都具有N个以上的元素,则该方法非常理想,但否则会增加排序成本。我的问题是,是否有另一种方法在两种情况下都能正常工作。理想情况下,它会执行以下操作

for each category {
   if # of element <= N:
      continue
   sort and get the top N
}

例如,但是有更好的SQL吗?

WITH table_with_count AS (
         SELECT Field1, Field2, RankCriteria, count() over (PARTITION BY Section) as c
         FROM table
),

rs AS (
    SELECT Field1,Field2, Rank() 
      over (Partition BY Section
            ORDER BY RankCriteria DESC ) AS Rank
    FROM table_with_count 
    where c > 10
) 

(SELECT Field1,Field2e FROM rs WHERE Rank <= 10)
     union
(SELECT Field1,Field2 FROM table_with_count WHERE c <= 10)

2 个答案:

答案 0 :(得分:1)

不,确实不应该存在。总体上,您在这里描述的是XY问题。

您似乎:

  • 担心排序,但实际上排序(带有可选的辅助排序)是对数据进行改组/重新分区的最有效方法,因为它不会导致文件描述符的泛滥。实际上,正是出于这个原因,Spark严格选择排序而不是其他选择(散列)。
  • 担心小组的“不必要”排序,而实际上问题是窗口函数的固有效率低下,这要求对所有数据进行完全混洗,因此表现出与臭名昭著的groupByKey相同的行为模式。

还有更有效的模式(MLPairRDDFunctions.topByKey是最突出的示例),但是这些模式尚未移植到Dataset API中,因此需要自定义Aggregator选择(例如通过分位数逼近),但这会增加数据传递的次数,并且在许多情况下不会提供任何性能提升。

答案 1 :(得分:0)

这个评论太长了。

没有这样的优化。基本上,使用开窗子句对 all 数据进行排序。我想数据库引擎实际上可以为partition by使用哈希算法,为order by使用排序算法,但是我不认为这是常见的方法。

无论如何,该操作遍及整个集合,因此应为此进行优化。尝试不对子集进行排序会增加很多开销-例如,对每个子集多次运行排序并计算每个子集中的行数。

还要注意,在窗口函数之后,在逻辑上与“ 3”进行比较。我不认为窗口函数通常会针对此类后过滤进行优化(尽管再一次,这是可能的优化)。