减去两列不同的表

时间:2022-03-16 15:41:37

I have two unrelated tables:

我有两个不相关的表:

contribution(id,amount, create_at, user_id)

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

I need to subtract the sum of the amount of the contribution and of the solicitude from a user, but that result can't to be negative.

我需要从用户中减去贡献量和关注度的总和,但结果不能是负数。

How can I do this? Function or query?
I tried this query:

我怎样才能做到这一点?功能还是查询?我试过这个查询:

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

3 个解决方案

#1


0  

I interpret your remark but that result can't to be negative as requirement to return 0 instead of negative results. The simple solution is GREATEST():

我解释你的评论,但结果不能为负,因为要求返回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;

Otherwise, I kept your original query, which is fine.

否则,我保留了原始查询,这很好。

For other cases with the possible result that no row could be returned I would replace with two sub-selects. But the use of the aggregate function guarantees a result row, even if the given user_id is not found at all. Compare:

对于可能导致无法返回行的其他情况,我将替换为两个子选择。但是使用聚合函数可以保证结果行,即使根本找不到给定的user_id也是如此。相比:

If the result of the subtraction would be NULL (because no row is found or the sum is NULL), GREATEST() will also return 0.

如果减法的结果为NULL(因为没有找到行或者总和为NULL),GREATEST()也将返回0。

#2


2  

You can add an outer query to check the total value:

您可以添加外部查询以检查总值:

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;

This solution is OK, but I suggest an alternative approach. Check how this query works:

这个解决方案没问题,但我建议采用另一种方法。检查此查询的工作原理:

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;

I made a simple test, just out of curiosity, on this setup:

出于好奇心,我在这个设置上做了一个简单的测试:

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);

Results (msecs):

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

#3


-1  

You have to join the tables

你必须加入这些表格

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: I forgot an alias

编辑:我忘了别名

#1


0  

I interpret your remark but that result can't to be negative as requirement to return 0 instead of negative results. The simple solution is GREATEST():

我解释你的评论,但结果不能为负,因为要求返回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;

Otherwise, I kept your original query, which is fine.

否则,我保留了原始查询,这很好。

For other cases with the possible result that no row could be returned I would replace with two sub-selects. But the use of the aggregate function guarantees a result row, even if the given user_id is not found at all. Compare:

对于可能导致无法返回行的其他情况,我将替换为两个子选择。但是使用聚合函数可以保证结果行,即使根本找不到给定的user_id也是如此。相比:

If the result of the subtraction would be NULL (because no row is found or the sum is NULL), GREATEST() will also return 0.

如果减法的结果为NULL(因为没有找到行或者总和为NULL),GREATEST()也将返回0。

#2


2  

You can add an outer query to check the total value:

您可以添加外部查询以检查总值:

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;

This solution is OK, but I suggest an alternative approach. Check how this query works:

这个解决方案没问题,但我建议采用另一种方法。检查此查询的工作原理:

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;

I made a simple test, just out of curiosity, on this setup:

出于好奇心,我在这个设置上做了一个简单的测试:

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);

Results (msecs):

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

#3


-1  

You have to join the tables

你必须加入这些表格

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: I forgot an alias

编辑:我忘了别名