Multiple Columns in Case using joins - mysql

I have a table "payments" and "payments1". There is a join on both the tables.
Payments:
-----------
id type amount values
1 A 10 x
2 B 20 y
2 A 30 z
I am trying to group by id and type. Such that I can get the results as
id type total_amount type1 total_amount(sum)
-----------------------------------------------
1 A 10
2 A 20 B 30
I have tried following query
select
case when r.type = 'A' then #payment+sum(r.amount) end as total_amount,
case when r.type = 'B' then #refund+sum(r.amount) end as total_amount(sum)
from payments r
But in CASE it gets executed only for one type?

The question is a bit unclear but I assume you're looking for the group by statement.
select
case when r.type = 'A' then #payment+sum(r.amount) end as total_amount,
case when r.type = 'B' then #refund+sum(r.amount) end as total_amount(sum)
FROM payments r
GROUP BY r.type
for each distinct r.type value in the table there will be result row with the aggregation date in the select statement.
Also a different suggestion:
select
(case r.type when 'A' then #payment else #refund end)+sum(r.amount) as total_amount
FROM payments r
GROUP BY r.type

If types are fixed, think you need a query like this:
SELECT
id,
'A' as type,
SUM(CASE WHEN type='A' THEN amount END) sum_typeA,
'B' as type1,
SUM(CASE WHEN type='B' THEN amount END) sum_typeB
FROM
Payments
GROUP BY
id
Please see fiddle here.

Related

MySQL: Output ID based on SUM data in one column based attributes in another column

SQL Table:
Customer
Type
Payment
1
Apples
5
1
Apples
5
1
Oranges
1
1
Oranges
2
2
Apples
7
2
Oranges
3
2
Oranges
6
Based on the above, looking to determine which customers have paid more for apples compared to oranges as a sum of all their payments.
In the case of the above table,
Customer 1 - Apples 10 > Oranges 3
Customer 2 - Apples 7 < Oranges 9
Thus the SQL should output Customer 1
I have attempted multiple queries, with the following as the most promising but getting an invalid use of group function error code 1111.
SELECT a.customer
FROM (SELECT customer, SUM(payment) AS orangespaid FROM table
WHERE type ='Oranges'
GROUP BY customer) o
JOIN table AS a ON a.customer = o.customer
WHERE type = 'Apples' and SUM(payment) > orangespaid
GROUP BY customer
ORDER BY customer;
There are a lot of ways to achieve that.
Here's how you do without sub-query:
SELECT Customer,
SUM(CASE WHEN Type='Apples' THEN Payment ELSE 0 END) AS Apples,
SUM(CASE WHEN Type='Oranges' THEN Payment ELSE 0 END) AS Oranges
FROM table1
GROUP BY Customer
HAVING Apples > Oranges;
Or like this:
SELECT Customer,
SUM(IF(Type='Apples',Payment,0)) > SUM(IF(Type='Oranges',Payment,0)) Chk
FROM table1
GROUP BY Customer
HAVING Chk=1
Or a slight modification of the query above, instead of checking the value in SELECT then filter from HAVING, why not just directly do the checking in HAVING:
SELECT Customer
FROM table1
GROUP BY Customer
HAVING SUM(IF(Type='Apples',Payment,0)) > SUM(IF(Type='Oranges',Payment,0)) != 0;
The first query can also be done in similar way.
Demo fiddle
Side note:
As for the difference between using CASE or IF, it's basically operates the same so it's more to individual preference. I mostly opt to use CASE because of readability and easier to edit (not much usage of parentheses/brackets) but using IF almost every time is shorter to write.
Try moving the SUM into a second subquery instead
SELECT a.customer
FROM (SELECT customer, SUM(payment) AS orangespaid FROM table
WHERE type ='Oranges'
GROUP BY customer) o
JOIN (SELECT customer, SUM(payment) AS applespaid FROM table
WHERE type ='Apples'
GROUP BY customer) AS a ON a.customer = o.customer
WHERE applespaid > orangespaid
ORDER BY customer;
You should try with sum(case when) for each type you want, it might not the best solution but it works.
select a.customer
from (select as1.Customer,
sum(case when type = 'Oranges' then payment else 0 end) AS orangespaid,
sum(case when type = 'Apples' then payment else 0 end) AS applespaid
from as1 group by as1.Customer) a
where applespaid > orangespaid
dbfiddle here

Finding difference of two columns with where clause - MySQL

I'm trying to get the difference of two columns(OTHER AND SENIOR) with WHERE clause along with the Sum of their amount. The formulas are working fine, however, I'm not getting the result I want.
I tried to do it using INNER JOIN and unfortunately its not the result that I expected
Here's my query:
select a.ID as EMPLOYEE,
sum(a.amount) as other,
sum(b.amount) as senior,
(sum(a.amount) - sum(b.amount)) as result
from gndsale a
INNER join gndsale
b ON a.ID= b.ID
where a.TYPE = "5" and b.seniortype = "10"
group by a.ID
Result:
What I want to do is get the difference of Column (Other) where type = 5 and Column (Senior) where seniortype = 10
Here is my query for Senior:
select ID as EMPLOYEE, sum(amount)
from gndsale
where seniortype = "10"
group by ID
Result:
Here is my query for Other:
select ID as employee, sum(amount)
from gndsale
where type = "5"
group by ID
The result should be
9907 = 530
9912 = 63.71
Anyone can help me with this? :(
Sample/Expected output:
You have chosen a very bad name for the column. In spite of its name ID is not an ID to identify a record in the table.
You are probably looking for something like the following, an aggregation per ID where you sum type 5 and seniortype 10 separately:
select
a.ID as employee,
coalesce(sum(case when type = 5 then amount end), 0) as other,
coalesce(sum(case when seniortype = 10 then amount end), 0) as senior,
coalesce(sum(case when type = 5 then amount end), 0) -
coalesce(sum(case when seniortype = 10 then amount end), 0) as result
from gndsale
group by a.id
having sum(type = 5) > 0 and sum(seniortype = 10) > 0;
(The HAVING clause ensures only to get IDs that have both records with type = 5 and seniortype = 10. We use MySQL's true = 1 / false = 0 here. Remove it, if you want other IDs, too. If you keep it and value cannot be null, then you can remove the coalesces instead.)
You might give this a try:
SELECT
a.`employee`,
sum(a.`amount`) - sum(b.`amount`) as total
FROM `gndsale` a
JOIN `gndsale` b
ON a.`employee` = b.`employee` and b.`senior_type` = "10"
WHERE a.`type` = "5"
GROUP BY a.`employee`
ORDER BY a.`employee`;
It simply joins your two queries.
Please try this query
select employee, other, senior, (other-senior) AS result
From (
select ID as employee,
sum(if(type=5,amount,0)) AS other,
sum(if(seniortype =10,amount,0)) AS senior
from gndsale group by ID
) a

Comparing day and week prices and getting cheapest

I'm trying to get the lowest price of an object. Problem is there can be daily and weekly prices. So when searching for the cheapest price i have to multiply the day price time 7 and compare to the week price to get the cheapest.
It can also happen that an object has week prices only or day prices only (or no prices at all).
BTW: It has to be such a subselect query, cause i have some more WHERE queries following later.
Pricetable
id price type oid
1 10 d 1
2 12 d 2
3 70 w 1
4 80 w 2
Objects
id name
1 house1
2 house2
This is what i'm using but its not working correctly. When the day price*7 is bigger that the week price it still gives me the day price.
SELECT p.oid, p.price, p.id, p.type FROM Pricetable p INNER JOIN (
SELECT oid, MIN(IF(type="w",price, price*7)) AS price, id, type
FROM Pricetable
GROUP BY oid
) p2 ON p.oid = p2.oid AND p.id= p2.id
Your query should work to get the minimum price. However, it should be written as:
SELECT oid, MIN(CASE WHEN type = 'w' THEN price ELSE 7*price END) AS price
FROM Pricetable
GROUP BY oid ;
If you want other values from row with the minimum price, then you need more logic. How about this?
SELECT pt.*oid, MIN(CASE WHEN type = 'w' THEN price ELSE 7*price END) AS price
FROM Pricetable pt
WHERE pt.id = (SELECT pt2.id
FROM PriceTable pt2
WHERE pt2.oid = pt.oid
ORDER BY (CASE WHEN pt2.type = 'w' THEN pt2.price ELSE 7*pt2.price END)
LIMIT 1
);
You'll need a sub-select with some sort of ranking to get the cheapest price.
Check https://dba.stackexchange.com/questions/13703/get-the-rank-of-a-user-in-a-score-table That should get you on the right track.
I don't have access to MySQL right now, and it's a bit different than MSSQL. But that link should set you on the path.

SQL Query to change group clause

I have a query as follows:
SELECT age_groups.Name as name, avg(scores.score) as avg
FROM scores
JOIN users ON scores.id = users.id
JOIN age_groups on user.age_group = age_groups.id;
There are 5 age groups: 0-10, 11-20, 21-30, 31-40, 41-50.
I'd like to have only three age groups in my results: 0-30, 31-40, and 41-50.
What statements would allow me to group three age groups together?
EDIT:
The age_groups table looks like this:
ID - Name
1 - 0-10
2 - 11-20
3 - 21-30
...etc
You can use case to combine age groups. First, though, your query is missing group by, so it should read more like:
SELECT ag.Name as name, avg(s.score) as avg
FROM scores s JOIN
users u
ON s.id = u.id JOIN
age_groups ag
on u.age_group = ag.id
GROUP BY ag.Name;
You can then do what you want as:
SELECT (case when ag.Name in ('0-10', '11-20', '21-30') then '0-30'
else ag.Name
end) as MyAgeGroup, avg(s.score) as avg
FROM scores s JOIN
users u
ON s.id = u.id JOIN
age_groups ag
on u.age_group = ag.id
GROUP BY (case when ag.Name in ('0-10', '11-20', '21-30') then '0-30'
else ag.Name
end);
One solution is to replace "age_groups.Name" in your select clause with a case statement which remaps each of the contributing ranges to your desired range:
CASE age_groups.Name
WHEN '0-10'
THEN '0-30'
WHEN '10-20'
THEN '0-30'
WHEN '20-30'
THEN '0-30'
ELSE age_groups.Name
END AS Name
and also add this to the bottom with a GROUP BY clause (e.g., GROUP BY CASE WHEN ...)
However, if this type of thing might be done frequently, then I would recommend a longer term solution, which is to add numeric bounds to the age groups in your age group table such as Age_Group_Lower_Bound (integer) and Age_Group_Upper_Bound (integer) (e.g., your 0-10 row would then have Age_Group_Lower_Bound=0 and Age_Group_Upper_Bound=10). Once these are in place, your original query could use these, e.g.,:
CASE
WHEN age_groups.Age_Group_Upper_Bound <= 30
THEN '0-30'
ELSE age_Groups.Name
END
Here's an example of that, taking it further with more ranges getting grouped together, e.g.:
CASE
WHEN age_groups.Age_Group_Upper_Bound <= 20
THEN '0-20'
WHEN age_groups.Age_Group_Upper_Bound <= 40
THEN '20-40'
WHEN age_groups.Age_Group_Upper_Bound <= 60
THEN '40-60'
ELSE '60+'
END

Adding columns resulting from GROUP BY clause

I got stuck in a scenario where I need to add columns from two rows after using GROUP BY.
My query goes as:
select AcctId,DC,sum(TrnAmt) from TableId
group by AcctId,DC
order by AcctId,DC
Result:-
VcrAcctId DrCr SumTranAmt
51 C 37469
51 D 37000
My expected result is:
VcrAcctId Actual
51 37469-37000
How can I get my expected result in the same query?
Based on discussion in the comments I think you need
SELECT AcctId,
Sum(CASE DC
WHEN 'C' THEN TrnAmt
WHEN 'D' THEN -TrnAmt
ELSE 0
END) AS DC
FROM TableId
GROUP BY AcctId
ORDER BY AcctId
If in fact you do want the credits and debits split out you can use
SELECT AcctId,
Sum(CASE
WHEN DC = 'C' THEN TrnAmt
ELSE 0
END) AS C,
Sum(CASE
WHEN DC = 'D' THEN TrnAmt
ELSE 0
END) AS D
FROM TableId
GROUP BY AcctId
ORDER BY AcctId
You could cast the SUM expressions to varchar and concatenate them to get the results shown in the question if that is what is actually needed.
This is assuming Actual is ordered on DrCR, so that C result row comes before D result row.
Also, replace Table1 with a CTE from your original query, so that you first sum up per VcrAcctId and DC.
SELECT VcrAcctId, STUFF((SELECT '-' + convert(varchar(100),SumTranAmt)
FROM Table1 I
WHERE Table1.VcrAcctId = I.VcrAcctID
ORDER BY DrCr
FOR XML PATH ('')),1,1,'')
FROM Table1
Group by VcrAcctId
SQL Fiddle Demo