窗口函数:仅对另一列中的不同值求和
Posted
技术标签:
【中文标题】窗口函数:仅对另一列中的不同值求和【英文标题】:Window function: Summing for only distinct value in the other column 【发布时间】:2019-11-01 14:06:54 【问题描述】:注意:这个问题似乎得到了不错的看法,所以我认为最好更新这个问题以清楚起见。大多数更改都是装饰性的,但唯一的主要更改是我在 weights_table 中添加了月份列。权重表是月度表,所以从技术上讲这并不重要,但我想在两个表中都有月份列会使表关系更加明显和合乎逻辑
问题
我有一个使用两个表 [person_table] 和 [weights_table] 的查询。
select
a.month,
a.movie,
count(a.person_id) as raw,
sum(b.weight) as weighted,
sum(b.weight)/sum(sum(b.weight)) over () as share -- I need to change this calculation
from (select distinct month,
movie,
person_id
from person_table) a
join weights_table b on a.month=b.month and a.person_id=b.person_id
group by a.month, a.movie;
我想要更改最后一个计算,以便将分母 sum(sum(b.weight)) over ()
计算为 (distinct person_ids per month) 的权重总和,而不是 (distinct person_ids per movie per月)。有没有更简单的方法来适应这种情况而无需添加另一个子查询?
person_table 示例
+-------+-------+-----------+
| month | movie | person_id |
+-------+-------+-----------+
| 1 | a | 1 |
| 1 | b | 1 |
| 1 | b | 2 |
| 1 | a | 2 |
| 1 | c | 3 |
| 1 | d | 4 |
| 1 | a | 2 |
| 1 | c | 3 |
| 1 | a | 6 |
+-------+-------+-----------+
weights_table 示例
+-------+-----------+--------+
| month | person_id | weight |
+-------+-----------+--------+
| 1 | 1 | 12 |
| 1 | 2 | 34 |
| 1 | 3 | 65 |
| 1 | 4 | 76 |
| 1 | 7 | 96 |
+-------+-----------+--------+
DDL Fiddle
预期结果
+-------+-------+-----+----------+-------+
| month | movie | raw | weighted | share |
+-------+-------+-----+----------+-------+
| 1 | a | 2 | 46 | 0.25 | --(12+34)/(12+34+65+76)=0.25
| 1 | b | 2 | 46 | 0.25 |
| 1 | c | 1 | 64 | 0.35 |
| 1 | d | 1 | 76 | 0.41 |
+-------+-------+-----+----------+-------+
指标定义:
原始:每部电影每月所有不同 person_id 的计数)
加权:每部电影每月不同 person_id 的权重总和。
分享:加权与(每月与persons_table匹配的不同person_id的权重总和)的比率
【问题讨论】:
样本数据和期望的结果真的很有帮助——就像定义你想要计算的指标一样。 让我添加它们 所以,如果我理解这一点。如果一个人在一个月内不止一次看同一部电影,您是否希望您的原始和加权只计算一次?但是,您希望所有这些都用于共享。对吗? @MikeWalton 对于原始和加权,这是正确的,但如果同一个人观看另一部电影,则计为 2。但对于份额的分母,他们观看的电影无关紧要。我想总结当月唯一不同 person_id 的相应权重。 能否请您向我们展示您对该样本数据的预期结果? 【参考方案1】:啊,表中只有一个月的数据,并将子选择分解为 CTE 以查看是否可以看到模式。我没有看到任何.. 因此,这似乎是您喜欢 SQL 的方式(对我而言)
with person_table as (
select column1 as month, column2 as movie, column3 as person_id, column4 as unique_visit_id
from values (1, 'a', 1, 1),
(1, 'b', 1, 2),
(1, 'b', 2, 3),
(1, 'a', 2, 4),
(1, 'c', 3, 5),
(1, 'd', 4, 6),
(1, 'a', 2, 7),
(1, 'c', 3, 8),
(1, 'a', 6, 9)
), weight_table as (
select column1 as person_id, column2 as weight
from values (1, 12), (2, 34), (3, 65), (4, 76), (999,999)
), dis_month_people as (
select distinct month, person_id
from person_table
), month_share as (
select month, sum(weight) as total_weight
from dis_month_people dp
join weight_table w on dp.person_id = w.person_id
group by 1
), dis_month_movie_people as (
select distinct month, movie, person_id
from person_table
)
select t.* --, weighted, total_weight
,t.weighted/m.total_weight as share
from (
select
a.month,
a.movie,
count(a.person_id) as raw,
sum(b.weight) as weighted
from dis_month_movie_people a
join weight_table b on a.person_id = b.person_id
group by 1,2
) AS t
join month_share m on t.month = m.month
order by 1,2;
【讨论】:
【参考方案2】:可能是这样的:
select a.month,
a.movie,
count(a.person_id) as raw,
sum(b.weight) as weighted,
100*weighted/c.ttl_weight as share
from (select distinct month, movie, person_id from person_table) a
inner join weights_table b on a.person_id=b.person_id
cross join (select sum(weight) as ttl_weight from weights_table w
where exists (select 1
from person_table p
where w.person_id=p.person_id)
) c
group by a.month, a.movie, c.ttl_weight
;
【讨论】:
如果您将用户行添加到权重中,则月度数据中不存在该行的总和。【参考方案3】:万一这个丑陋的解决方法对任何人都有帮助 - 我所做的是缩小子查询/CTE 中的权重,以模拟在外部查询中对唯一权重求和的效果。
select month,
movie,
count(distinct person_id) as raw,
sum(w1) as weighted,
sum(w1)/1.0/sum(sum(w2)) over() as share
from (select a.*,
b.weight/count(*) over (partition by a.month, a.movie, a.person_id) w1,
b.weight/count(*) over (partition by a.month, a.person_id) w2
from person_table a
join weights_table b on a.month=b.month and a.person_id=b.person_id) t
group by t.month, t.movie;
我不能说我为这个解决方案感到自豪,因为它只有在我经常查询此类数据时才有用,在这种情况下,将子查询的结果存储在永久月度表中是有意义的。但由于我每月只使用一次或两次,因此我更倾向于使用更高效的查询结构,即使以冗长为代价。
【讨论】:
此代码实际上不起作用,因为您的“月”份额 (total_weight ) 适用于所有月份。select t.*, weighted, total_weight, weighted/total_weight as share
显示总数始终为 187,这不是您所描述的。
sigh 在示例数据中只有一个月,所以它是正确的,但是跨多个月这个代码是错误的。以上是关于窗口函数:仅对另一列中的不同值求和的主要内容,如果未能解决你的问题,请参考以下文章