Функция окна: суммирование только для другого значения в другом столбце - PullRequest
0 голосов
/ 01 ноября 2019

У меня есть этот запрос, который использует две таблицы [person_table] и [weights_table]. Идея состоит в том, чтобы рассчитать необработанное количество, взвешенные метрики для каждого фильма.

select 
a.month,
a.movie,
count(a.person_id) as raw,
sum(b.weight) as weighted,
100.0*weighted/sum(weighted) over () 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
group by a.month, a.movie;

Я хочу, чтобы знаменатель (sum(weighted) over ()) суммировал веса только для отдельных person_ids за месяц, независимо от того, какой фильм они смотрели. В настоящее время этого не происходит из-за distinct month, movie, person_id, который у меня есть в первой таблице. Но мне это нужно для других показателей, включая числитель. Есть ли лучший способ справиться с этим, не используя слишком много подзапросов?

Образец person_table

month   movie   person_id unique_visit_id
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

person_id   weight
1           12
2           34
3           65
4           76

Определение метрики:

Raw : количество всех (различных person_ids в месяц на фильм)

Взвешенный : Сумма всех весовых коэффициентов (различных person_ids в месяц за фильм). Не думайте о весе как о факторе

Доля : отношение Взвешенное к (Сумма весов только отдельных person_ids в этом месяце, независимо от фильма)

Ожидаемый результат для фильма a, например:

month  movie  raw   weighted      share
1      a      2     12+34        12+34/12+34+65+76

Примечание. Данные в обеих таблицах относятся к конкретному месяцу, поэтому столбец месяца будет одинаковымчерез таблицу, и я использовал его в своем подходе к этой проблеме с несколькими CTE.

Ответы [ 3 ]

1 голос
/ 02 ноября 2019

Ах, только с данными за один месяц в таблице и разложением подвыбора на 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;
0 голосов
/ 01 ноября 2019

Я закончил переделывать свой стол, чтобы достичь той простоты, к которой стремился, но в случае, если мой уродливый обходной путь кому-нибудь поможет, ниже приведены два подхода.

select t.*, weighted/total_weight as share
from
(select 
a.month,
a.movie,
count(a.person_id) as raw,
sum(b.weight) as weighted,
from (select distinct month, movie, person_id from person_table) a 
inner join weights_table b on a.person_id=b.person_id
group by a.month, a.movie) t
left join (select sum(weight) as total_weight from weights_table e  
where exists (select 1 from persons_table d where d.person_id=e.person_id));

Еще один метод, где я могу просто предварительно масштабировать свои веса для достижения того же результата. Это приводит к дополнительному столбцу.

select 
t.month,
t.movie,
count(distinct t.person_id) as raw,
sum(t.weight) as weighted,
sum(t.uniq_person_weight) as total_weight,
weighted/sum(total_weight) over() as share
from 
(select a.month, a.movie, a.person_id, 
        b.weight/count(*) over (partition by a.movie, a.person_id) as weight, 
        b.weight/count(*) over (partition by a.person_id) as uniq_person_weight
        from person_table a inner join weights_table b on a.person_id=b.person_id) t
group by t.month, t.movie
0 голосов
/ 01 ноября 2019

Может быть что-то вроде:

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