Оконная функция: суммирование только для отдельного значения в другом столбце

Примечание. Похоже, этот вопрос получил приличное количество просмотров, поэтому я подумал, что лучше обновить этот вопрос для ясности. Большинство изменений носит косметический характер, но единственное серьезное изменение состоит в том, что я добавил столбец месяца в 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 () рассчитывался как сумма суммы весов для (различных person_ids в месяц) вместо суммы суммы весов для (различных person_ids на фильм в месяц). Есть ли более простой способ учесть это, не добавляя еще один подзапрос?

Образец 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 |
+-------+-------+-----------+

Образец таблицы весов

+-------+-----------+--------+
| 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 за месяц для каждого фильма).

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


person Rajat    schedule 01.11.2019    source источник
comment
Выборочные данные и желаемые результаты действительно помогут - как и определения показателей, которые вы хотите рассчитать.   -  person Gordon Linoff    schedule 01.11.2019
comment
Позвольте мне добавить их   -  person Rajat    schedule 01.11.2019
comment
Итак, если я это понимаю. Вы хотите, чтобы ваши необработанные и взвешенные считали человека только один раз, если он смотрел один и тот же фильм более одного раза в месяц? Однако вы хотите получить их всех вместе. Это правильно?   -  person Mike Walton    schedule 01.11.2019
comment
@MikeWalton Для необработанных и взвешенных - это правильно, но если тот же человек смотрит другой фильм, это считается как 2. Но для знаменателя доли не имеет значения, какой фильм они смотрят. Я хочу суммировать соответствующие веса только отдельных person_id за месяц.   -  person Rajat    schedule 01.11.2019
comment
Не могли бы вы показать нам результат, которого вы ожидаете от этих выборочных данных?   -  person GMB    schedule 01.11.2019


Ответы (3)


Ах, с данными всего за один месяц в таблице и разложением подвыбора на 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;
person Simeon Pilgrim    schedule 01.11.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
;
person Keith    schedule 01.11.2019
comment
если вы добавляете пользовательскую строку к весу, которой нет в ежемесячных данных, эта строка суммируется. - person Simeon Pilgrim; 02.11.2019

В случае, если этот уродливый обходной путь кому-то поможет - я уменьшил вес в подзапросе / 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;

Я не могу сказать, что горжусь этим решением, потому что оно полезно только в том случае, если я запрашиваю такие данные на регулярной основе, и в этом случае сохранение результата подзапроса в постоянных ежемесячных таблицах имеет смысл. Но поскольку я использую это только один или два раза в месяц, я был более склонен иметь более эффективную структуру запросов, даже ценой подробностей.

person Rajat    schedule 01.11.2019
comment
этот код на самом деле не работает, так как ваши месячные доли (total_weight) указаны для всех месяцев. select t.*, weighted, total_weight, weighted/total_weight as share показывает, что общее количество всегда 187, что не то, что вы описываете. - person Simeon Pilgrim; 02.11.2019
comment
вздох в данных примера есть только один месяц, так что это правильно, но через много месяцев этот код неверен. - person Simeon Pilgrim; 02.11.2019