2015-06-15 3 views
1

у меня есть два несвязанных таблицы:Вычтите две колонок разных таблиц

contribution(id,amount, create_at, user_id) 

solicitude(id, amount, create_at, status_id, type_id, user_id) 

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

Как это сделать? Функция или запрос?
Я попробовал этот запрос:

SELECT sum(contribution.amount) 
- (SELECT sum(solicitude.amount) 
    FROM solicitude 
    WHERE user_id = 1 AND status_id = 1) as total 
FROM contribution 
WHERE contribution.user_id = 1 
+0

'но этот результат не может быть отрицательным'. Пожалуйста, дополните. И что случилось с запросом, который у вас есть? Синтаксис можно упростить, но он отлично работает. –

ответ

0

Я интерпретирую ваше замечание but that result can't to be negative как требование вернуть 0 вместо отрицательных результатов. Самое простое решение GREATEST():

SELECT GREATEST(sum(amount) 
     - (SELECT sum(amount) 
     FROM solicitude 
     WHERE status_id = 1 
     AND user_id = 1), 0) AS total 
FROM contribution 
WHERE user_id = 1; 

В противном случае, я сохранил свой первоначальный запрос, который отлично.

Для других случаев с возможным результатом, чтобы никакая строка не могла быть возвращена, я заменил бы двумя подвыборками. Но использование агрегатной функции гарантирует строку результата, даже если данный user_id не найден вообще. Сравните:

Если результат вычитания будет NULL (потому что ни одна строка не найдена или сумма NULL), GREATEST() также вернется 0.

-1

Вы должны присоединиться к таблицам

SELECT sum(c.amount) - s.total 
FROM contribution c inner join (SELECT user_id, sum(solicitude.amount) total 
FROM solicitude GROUP BY c.user_id HAVING user_id = 1 AND status_id = 1 )s 
on c.user_id=s.user_id GROUP BY c.user_id,s.total HAVING c.user_id = 1 

EDIT: Я забыл псевдоним

+0

Спасибо за ваше время;) –

2

Вы можете добавить внешний запрос, чтобы проверить общее значение:

SELECT CASE WHEN total > 0 THEN total ELSE 0 END AS total 
FROM (
    SELECT 
      sum(contribution.amount) - (SELECT sum(solicitude.amount) 
      FROM solicitude 
      WHERE user_id = 1 AND status_id = 1) as total 
     FROM contribution 
     WHERE 
     contribution .user_id = 1 
    ) alias; 

Это решение в порядке, но я предлагаю альтернативный подход глава Проверьте, как работает этот запрос:

with contribution as (
    select user_id, sum(amount) as amount from contribution 
    group by 1), 
solicitude as (
    select user_id, sum(amount) as amount from solicitude 
    where status_id = 1 
    group by 1) 
select 
    c.user_id, c.amount as contribution, s.amount as solitude, 
    case when c.amount > s.amount then c.amount - s.amount else 0 end as total 
from contribution c 
join solicitude s on c.user_id = s.user_id; 

Я сделал простой тест, просто из любопытства, на этой установке:

create table public.solicitude (
    id integer, 
    amount numeric, 
    create_at timestamp without time zone, 
    status_id integer, 
    type_id integer, 
    user_id integer 
); 

create table public.contribution (
    id integer, 
    amount numeric, 
    create_at timestamp without time zone, 
    user_id integer 
); 

insert into contribution (user_id, amount) 
select (random()* 50)::int, (random()* 100)::int 
from generate_series(1, 4000000); 

insert into solicitude (user_id, amount, status_id) 
select (random()* 50)::int, (random()* 100)::int, 1 
from generate_series(1, 4000000); 

Результаты (мс):

Erwin's solution with greatest(): 922, 905, 922, 904, 904, 904, 905, 912, 905, 922 
My solution with an outer query: 796, 795, 814, 814, 815, 795, 815, 796, 815, 796 
+0

Я думаю, что это дороже и подробней, чем нужно. –

+0

@ErwinBrandstetter - Вы имеете в виду второй запрос? – klin

+0

@klin: Да, второе, в частности, CTE стоят дороже и не нужны здесь. Но также первый: еще один уровень запроса, чем необходимо, и 'GREATEST()' будет упрощаться дальше. –

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