2016-02-02 4 views
0

Соответствующая часть моей схемы базы данных выглядит следующим образом (Ruby на Rails миграции кода, но должны быть легко читать):Проблемы с SQL запроса с использованием SUM

create_table "team_memberships" do |t| 
    t.integer "team_id" 
    t.integer "user_id" 
end 

create_table "users" do |t| 
    t.integer "id" 
    t.string "slug" 
end 

create_table "performance_points" do |t| 
    t.integer "id" 
    t.integer "user_id", 
    t.date "date", 
    t.integer "points", 
    t.integer "team_id" 
end 

Я хочу, чтобы запрос, который возвращает список пользователей, отсортированных по общей сумме очков производительности, которые они получили с определенной даты. Обратите внимание, что один «performance_points» строка не равна одной точке, мы должны подвести «точки»

Запрос я до сих пор выглядит следующим образом:

SELECT u.id, u.slug, SUM(pp.points) AS total 
FROM users u 
JOIN performance_points pp ON pp.user_id = u.id 
JOIN team_memberships tm ON tm.team_id = pp.team_id AND tm.user_id = pp.user_id 
WHERE (pp.date > '2015-08-02 13:57:14.042221') 
GROUP BY pp.id, u.id 
ORDER BY total DESC 
LIMIT 50 

Первые три результата являются:

"id","slug","total" 
32369,"andreas-jensen-9de10dec-0f88-427f-b135-62cebea611c8",245 
23752,"kenneth-kjaerstad",95 
34179,"marius-mork-rydal",93 

Для проверки правильности результатов подсчета очков для каждого пользователя. Однако второй кажется неправильным. Я запустить этот запрос с идентификатором Кеннета:

SELECT SUM(performance_points.points) 
FROM performance_points 
WHERE performance_points.user_id = 23752 
    AND (date > '2015-08-02 13:57:14.042221') 

я получаю: 84. Глядя на все точки производительности Kenneth с:

SELECT performance_points.points 
FROM performance_points 
WHERE performance_points.user_id = 23752 
    AND (date > '2015-08-02 13:57:14.042221') 

Получаем:

-10 + 1 - 2 + 95 действительно 84, так что я не знаю, что происходит с первого запроса. Почему сумма 95?

Я бегу PostgreSQL версии 9.3.5

+0

Все ли строки показателей производительности для одного и того же team_membership? –

+0

В случае с этим пользователем они есть, но это может быть не всегда так. Пользователи могут быть в нескольких командах и получать очки от каждого. –

+0

Просто замените в исходном запросе 'select ...' на 'select *' и удалите 'group by' clause и, я уверен, вы найдете эту проблему. – Abelisto

ответ

0

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

0

Try ниже запроса и дайте нам знать ответ, если он работает:

SELECT u.id, u.slug, SUM(pp.points) AS total 
FROM users u 
INNER JOIN (select user_id,date,team_id, SUM(points) as points from performance_points group by user_id,date,team_id) pp ON pp.user_id = u.id 
INNER JOIN (select team_id, user_id from team_memberships group by team_id, user_id) tm ON tm.team_id = pp.team_id AND tm.user_id = pp.user_id 
WHERE (pp.date > '2015-08-02 13:57:14.042221') 
GROUP BY u.id, u.slug 
ORDER BY total DESC 
LIMIT 50 
; 
+0

Это дает ошибку: «ERROR: column pp.team_id не существует». Если я добавлю 'team_id' в' select' первого «внутреннего соединения», я получаю: «ERROR: столбец« performance_points.team_id »должен появиться в предложении GROUP BY или использоваться в агрегатной функции». Не знаете, как это исправить. –

+0

У меня уже есть team_id в группе – minatverma

+0

Это все еще ошибки. –

1

Не видя все ваши данные, это немного трудно догадаться, , но, возможно, для предварительной обработки КТР точки производительности будет делать это:

with pp_totals as (
    select user_id, sum (points) as points 
    from performance_points 
    where date > '2015-08-02 13:57:14.042221' 
    group by user_id 
) 
SELECT 
    u.id, u.slug, pp.points AS total 
FROM 
    users u 
    JOIN pp_totals pp ON pp.user_id = u.id 
    JOIN team_memberships tm ON tm.user_id = u.user_id 
ORDER BY pp.points DESC 
limit 50 

Если это не делает этого, вы можете создать SQL Скрипки и разместить его на свой вопрос?

+0

Но почему team_memberships? (Надеюсь, мы услышим больше.) – philipxy

+0

@philipxy - Я тоже это заметил, но я катался с ним. Я изменил соединение, основываясь на некоторых предположениях – Hambone

2

Если slug является уникальным для каждого пользователя:

SELECT u.id, u.slug, SUM(pp.points) AS total 
FROM users u 
JOIN performance_points pp 
ON u.id = pp.user_id 
WHERE pp.date > '2015-08-02 13:57:14.042221' 
GROUP BY u.id, u.slug 
ORDER BY total DESC 
LIMIT 50 

В противном случае вы не SELECTslug может, потому что это не столбец группировки, так что есть несколько значений этого в каждой группе. Вы хотите, чтобы GROUP BY user_id в performance_points, чтобы получить total за user_id, затем JOIN с users, чтобы получить slug s.

SELECT id, slug, total 
FROM users 
JOIN (
    SELECT user_id, SUM(points) AS total 
    FROM performance_points 
    WHERE date > '2015-08-02 13:57:14.042221' 
    GROUP BY user_id) t 
ON id = user_id 
ORDER BY total DESC 
LIMIT 50 

(Это не понятно, почему вы JOIN та с team_membership. Предположительно performance_points (user_id,team_id) является внешним ключом в него, то есть все такие пары уже содержатся в нем.)

2

Я взял свой запрос и добавил фильтр ограничивается одним пользователем.Теперь вы должны увидеть четыре строки для пользователя Kenneth-kjaerstad:

SELECT u.id, u.slug, SUM(pp.points) AS total 
FROM 
    users u 
    JOIN performance_points pp ON pp.user_id = u.id 
    JOIN team_memberships tm ON tm.team_id = pp.team_id AND tm.user_id = pp.user_id 
WHERE pp.date > '2015-08-02 13:57:14.042221' and u.id = 23752 
GROUP BY pp.id, u.id 

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

Причина в том, что ваша группировка неверна, поскольку вы просто хотите получить общее количество пользователей. pp.id должен быть уникальным для каждой строки в ваших результатах, и бессмысленно иметь группу в этой колонке вообще.

Также отмечу, что в вашей связи с таблицей team_memberships не существует цели, если только вам не нужно гарантировать, что членство в команде существует для каждой пары идентификаторов пользователей и команд из таблицы точек. Вот исправление:

SELECT u.id, min(u.slug) as slug, SUM(pp.points) AS total 
FROM 
    users u 
    JOIN performance_points pp ON pp.user_id = u.id 
    JOIN team_memberships tm ON tm.team_id = pp.team_id AND tm.user_id = pp.user_id 
WHERE pp.date > '2015-08-02 13:57:14.042221' 
GROUP BY u.id 
ORDER by total desc 

Этот ответ по существу эквивалентен @philipxy и @ Hambone's. Как вы можете видеть, не обязательно использовать некоторые из построенных им конструкций. Надеюсь, мое объяснение того, что пошло не так, полезно, какой бы подход вы ни выбрали.

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