2013-08-13 9 views
2

У меня возникли проблемы с суммирование значения полей на основе другого значения.Сумма столбцов в зависимости от другого значения столбца

мне нужно SUM(activities.points) на основе activities.activity_type, если это used_points или added_points и поместить его в AS used_points/added_points.

Таблица деятельности:

id | subscription_id | activity_type | points 
-------------------------------------------------- 
1 |   1  | used_points | 10 
2 |   1  | used_points | 50 
3 |   1  | added_points | 20 
4 |   1  | added_points | 30 
5 |   2  | used_points | 20 
6 |   2  | used_points | 45 
7 |   2  | added_points | 45 
8 |   2  | added_points | 45 

Таблица подписки:

id |  name | current_points 
------------------------------------- 
1 | card_1 |  700 
2 | card_2 |  900 

Что мне нужно:

name | current_points | used_points | added_points 
----------------------------------------------------------- 
card_1 |   700  |  60  |  50 
card_2 |   900  |  65  |  90 

То, что я пытался:

SELECT 
    subscriptions.name, 
    subscriptions.current_points, 
    IF(activities.activity_type="used_points", SUM(activities.points), null) 
     AS used_points, 
    IF(activities.activity_type="added_points", SUM(activities.points), null) 
     AS added_points 
FROM activities 
JOIN subscriptions 
    ON activities.subscription.id = subscription.id 
GROUP BY subscriptions.name 

Это не так.

Благодаря

+0

Вы хотите использовать ** 'SUM (IF())' **. То есть, удалить агрегированную функцию 'SUM' из выражения' IF' и вместо этого переместить выражение 'IF' в' SUM'. Выражение IF должно работать на отдельных строках, SUM будет агрегировать значение, возвращаемое для каждой строки. – spencer7593

ответ

3

Вы хотите использовать SUM(IF()). Вы хотите добавить значения, возвращаемые из IF. Вы хотите, чтобы выражение IF оценивалось для каждой отдельной строки. Затем используйте агрегат SUM, чтобы добавить значение, возвращаемое для каждой строки.

Извлеките агрегат SUM изнутри выражения IF и вместо этого оберните IF внутри SUM.


Followup

Q Но почему SUM() внутри ПЧ не работает?

A Ну, это действительно работает. Это просто не работает так, как вы хотите, чтобы он работал.

Функция MySQL SUM является «совокупной» функцией. Он объединяет строки вместе и возвращает одно значение.

Для выражения этой формы: IF(col='foo',SUM(numcol),0)

Что MySQL делает это агрегирование всех строк в SUM, и возвращает одно значение.

Другие базы данных будут подгонять подгонку и выдать ошибку с ссылкой на неагрегат col в этом выражении.MySQL более снисходителен и относится к справочнику col, как к агрегату (например, MIN (col) или MAX (col) ... работающему над группой строк и возвращающему одно значение. В этом случае MySQL выбирает единственная строка образца. (Не определена, какая строка будет выбрана в качестве строки выборки.) Таким образом, ссылка на col выглядит как GET_VALUE_FROM_SAMPLE_ROW(col). После того, как агрегаты завершены, это выражение IF будет оцениваться один раз.

Если вы начинаете с этим запросом, это набор строк вы хотите работать.

SELECT s.name 
    , s.current_points 
    , a.activity_type 
    , a.points 
    , IF(a.activity_type='used_points',a.points,NULL) AS used_points 
    , IF(a.activity_type='added_points',a.points,NULL) AS added_points 
    FROM subscriptions s 
    JOIN activities a 
    ON a.subscription_id = s.id 

при добавлении предложения GROUP BY, что собирается объединить некоторые из этих строк вместе. То, что вы получите для неагрегатов, - это значения из строки выборки.

Попробуйте добавить GROUP BY s.name в запрос и посмотреть, что возвращается.

Также попробуйте добавить в некоторых агрегатов, таких как SUM (a.points)

SELECT s.name 
    , s.current_points 
    , a.activity_type 
    , a.points 
    , IF(a.activity_type='used_points',a.points,NULL) AS used_points 
    , IF(a.activity_type='added_points',a.points,NULL) AS added_points 
    , SUM(a.points) AS total_points 
    FROM subscriptions s 
    JOIN activities a 
    ON a.subscription_id = s.id 
GROUP BY s.name 

Наконец, мы можем добавить в выражениях в запросе в списке выбора:

 , IF(a.activty_type='used_points',SUM(a.points),NULL) AS if_used_sum 
    , IF(a.activty_type='added_points',SUM(a.points),NULL) AS if_added_sum 

Что мы обнаруживаем, что значение, возвращаемое из этих выражений, будет либо SUM (a.points), которое будет соответствовать total_points, либо оно будет NULL. И мы можем увидеть значение столбца activity_type, извлеченного из одной строки выборки для каждой группы, и мы можем видеть, что это выражение «работает», оно просто не делает то, что вы действительно хотите: для условного тест для каждой отдельной строки, возвращающий значение для точек или нуль, а затем суммируя это для группы.

+0

Да, похоже, это работает. Но почему 'SUM()' внутри 'IF' не работает? – Vucko

+0

SUM оценивается для ВСЕХ строк, объединенных вместе, и возвращает одно значение. Если это внутри IF, то функция IF вычисляется только один раз, для одного значения, возвращаемого функцией SUM. Другие базы данных могли бы подгонять (вызывать ошибку) с вашим оператором SQL, потому что другое выражение в IF не является совокупностью. MySQL более снисходителен и выберет одну, примерную строку из всех строк, которые были включены в агрегат SUM, и использовать значение из строки выборки, как если бы это была совокупность. – spencer7593

+0

Спасибо за подробный ответ. – Vucko

1

изменения Try

IF(activities.activity_type="used_points", null, SUM(activities.points)) 
    AS used_points, 
IF(activities.activity_type="added_points", null, SUM(activities.points)) 
    AS added_points 

К следующим

SUM(IF(activities.activity_type="used_points", activities.points, 0)) 
    AS used_points, 
SUM(IF(activities.activity_type="added_points", activities.points, 0)) 
    AS added_points 

В этом случае вы проверить столбцы и суммовые точки или 0

+2

@BenC. Этот вопрос касается MySQL, а не SQL Server. –

+0

Так оно и есть. Я удалю мои комментарии. –

1

Ваш код лишь немного из:

SELECT 
    subscriptions.name, 
    subscriptions.current_points, 
    SUM(IF(activities.activity_type="used_points", 0, activities.points)) 
     AS used_points, 
    SUM(IF(activities.activity_type="added_points", 0, activities.points)) 
     AS added_points 
FROM activities 
JOIN subscriptions 
    ON activities.subscription_id = subscription.id 
GROUP BY subscriptions.name, subscriptions.current_points 

Примечание фиксированной опечатка во второй последней строке - Вы писали subscription.id вместо subscription_id. Кроме того, вы только сгруппированы по имени вместо имени и current_points, не уверены, что это разрешено в mysql (я использую T-SQL), это хорошая практика иметь его там в любом случае.

1

Ну, я сделал это, не используя инструкцию IF. Вот пример (http://sqlfiddle.com/#!2/076c3f/12):

SELECT 
    subs.name, 
    subs.current_points, 
    (SELECT SUM(points) FROM activities WHERE type = 1 AND subs_id = subs.id) AS used_points, 
    (SELECT SUM(points) FROM activities WHERE type = 2 AND subs_id = subs.id) AS added_points 
FROM activities 
JOIN subs ON activities.id = subs.id 
GROUP BY subs.name 

Примечание: Я изменил тип с VARCHAR в INT упростить.

+1

Указанный набор результатов может быть получен с использованием коррелированных подзапросов, как показано в вашем примере. Но коррелированные подзапросы обычно менее эффективны, чем другие альтернативы (для большого количества строк). Один из вариантов заключается в использовании выражения 'SUM (IF (type = 1, points, 0)) AS used_points' в списке SELECT. Это не использует подзапрос. – spencer7593

+0

Я вижу. Спасибо за объяснение. Я не думал об эффективности альтернатив в качестве основного фактора. Я постараюсь проверить, насколько эффективна эффективность. – Minoru

+1

Для таблицы ** activity ** с 64 строками среднее время для связанных с подзапросами _ составляет около 5 мс, тогда как среднее время с использованием выражения _IF равно 1 мс (возможно, меньше, но движок не использует плавающие точки на время). – Minoru

Смежные вопросы