2015-01-03 3 views
0

В моей базе данных есть таблица: Job. Каждое задание может содержать задачи (один для многих) - и одна и та же задача может быть повторно использована для нескольких заданий. Поэтому существует таблица Task и JobTask (таблица соединений для отношения «многие ко многим»). Существует также таблица Payment, в которой записаны полученные платежи (с помощью столбца JobId для отслеживания того, к какой задаче относится оплата). Потенциально может быть больше одного платежа, приписываемого заданию.Запрос SUM значения из объединенной таблицы - возвращает умноженный результат

Использование SQL Server 2012, у меня есть запрос, который возвращает краткое описание рабочих мест (общая стоимость работы, общая сумма полученных):

select j.JobId, 
sum(t.Rate) as [TotalOwedP], 
sum(p.Amount) as [TotalReceivedP] 

from Job j 
left outer join Payment p on j.JobId=p.JobId 
left outer join JobTask jt on j.JobId=jt.JobId 
left outer join Task t on jt.TaskId=t.TaskId 

group by j.JobId 

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

В моей тестовой базе данных у меня есть одна работа. Это задание назначено на шесть задач. Он также имеет один платеж, назначенный ему (100 фунтов стерлингов - хранится как 10000).

Используя приведенный выше запрос по этим данным, столбец TotalReceivedP относится к 60000, а не 10000. Кажется, это умножает сумму платежа для каждой задачи, назначенной заданию. Ло, и вот, если я добавлю еще одну задачу на это задание (поэтому число задач теперь равно 7), столбец TotalReceivedP показывает 70000.

В моем запросе есть определенная проблема, но я просто не могу понять, что это такое. Любые острые глаза способны это заметить? Кажется, что-то не так с соединением.

ответ

1

Для каждого отдельного JobId, SUM(p.Amount) резюмирует же значение Amount для каждой Task записи, относящейся к Job записи с этим JobId. Если 6 записей относятся к определенному Job, то SUM(p.Amount) будет отображать сумму, умноженную на 6, если связаны 7 записей, тогда сумма умножается на 7 и так далее.

Поскольку для каждого Работы есть только один Оплата сумма, нет необходимости выполнять определенную сумму на p.Amount.Sth, как это даст вам желаемый результат:

select j.JobId, 
     sum(t.Rate) as [TotalOwedP], 
     max(p.Amount) as [TotalReceivedP] 

from #Job j 
left outer join #Payment p on j.JobId=p.JobId 
left outer join #JobTask jt on j.JobId=jt.JobId 
left outer join #Task t on jt.TaskId=t.TaskId 

group by j.JobId 

EDIT:

Поскольку платформа используется SQL Server очень аккуратный способ (ИМХО), чтобы получить сумму агрегатов CTEs:

;WITH TaskGroup AS (
    SELECT JobId, SUM(Rate) AS [TotalOwedP] 
    FROM #Task t 
    INNER JOIN #JobTask jt ON t.TaskId = jt.TaskId 
    GROUP BY JobId 
), PaymentGroup AS (
    SELECT JobId, SUM(Amount) [TotalReceivedP] 
    FROM #Payment 
    GROUP BY JobId 
) 
SELECT tg.JobId, tg.TotalOwedP, pg.TotalReceivedP 
FROM TaskGroup tg 
LEFT JOIN PaymentGroup pg ON tg.JobId = pg.JobId 

Я только догадываюсь о схеме таблиц, но приведенное выше должно дать вам то, что вы хотите. Первый CTE вычисляет Rate сумм за JobId, второй CTE Amount сумм за JobId, в последнем запросе используются оба CTE для объединения результатов в одну таблицу.

+0

Это будет работать отлично, но, к сожалению, 'max (p.Amount)' не может использоваться, поскольку может быть назначено несколько платежей на одну работу. Вид боли. –

+0

@Teifi Если это так, то какой из этих выплат вы хотите суммировать с вашим запросом? Все они, только один? Если один, то какой? –

+0

Это должно быть сумма всех сумм платежей с соответствующим «JobId». Я думал, что «левое внешнее соединение Payment p на j.JobId = p.JobId' решит это, но, по-видимому, нет. –

2

Используйте Sub-Select для расчета SUM:

select j.JobId, 
sum(t.Rate) as [TotalOwedP], 
(SELECT SUM(p.Amount) FROM Payment p WHERE j.JobId=p.JobId) as [TotalReceivedP] 
from Job j 
left outer join JobTask jt on j.JobId=jt.JobId 
left outer join Task t on jt.TaskId=t.TaskId 
group by j.JobId 
+0

это потенциально очень плохое производительность по сравнению с JOIN. Я думаю, что это пропустило проблему, см. Мой ответ ... – RobP

+0

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

0

проблема заключается в том, что ваш JOIN ограничение не должно быть достаточно конкретным, так что вы получаете слишком много строки результата. Вероятно, платежи также должны быть объединены в «Задачу»? Невозможно сказать, если вы не разместите все схемы таблиц.

+0

Я могу опубликовать схемы, если вы хотите, но это превратится в очень многословный вопрос. Вы бы хотели, чтобы я? –

0

Вы должны отделить запрос для TotalOwed от запроса для TotalReceived. TotalOwed основан на отдельных задачах, тогда как оплата связана с заданием, а не с задачей.

--first query 
select j.JobId, 
sum(t.Rate) as [TotalOwedP] 
from Job j 
left outer join JobTask jt on j.JobId=jt.JobId 
left outer join Task t on jt.TaskId=t.TaskId 
group by j.JobId 

--second query 
select j.JobId, 
sum(p.Amount) as [TotalReceivedP] 
from Job j 
left outer join Payment p on j.JobId=p.JobId 
group by j.JobId 
+0

Эти данные помещаются прямо в сетку, поэтому я не могу запускать их как отдельные запросы :( –