How do I sum amount for specific user in mysql query? - mysql

I have three tables I am working with:
users table contains users id, name and status
supply_history table contains id, users_id, amount and status
rejected_deal table contains id, users_id, rejected_amount and also status.
What I am trying to find is to sum up each users supply_amount and rejected amount where users status, supply_history status and rejected_deal status is 1.
Here is what I have tried so far.
SELECT users.id, users.name,
sum(supply_history.amount) as supply_amount,
sum(rejected_deal.rejected_amount) as rejected_amount
from users
inner join supply_history on supply_history.users_id=users.id
inner join rejected_deal on rejected_deal.users_id=users.id
where users.status='1' and rejected_deal.status='1' and supply_history.status='1'
GROUP by users.id;
My current query result is bellow.
id
name
supply_amount
rejected_amount
1
Skipper
50
20
2
Private
200
20
The result I am looking for
id
name
supply_amount
rejected_amount
1
Skipper
50
10
2
Private
100
20
Bellow is my three tables
users table.
id
name
status
1
Skipper
1
2
Private
1
supply_history table.
id
users_id
amount
status
1
1
20
1
2
1
30
1
3
2
100
1
rejected_deal table.
id
users_id
rejected_amount
status
1
2
15
1
2
2
5
1
3
1
10
1
How do I solve this query please help. Thank you.

You are joining supply_history rows and rejected_deal rows to user rows. But supply_history and rejected_deal are not directly related. For one user, you create thus all combinations of supply_history and rejected_deal. The result is:
id
name
status
sh_id
users_id
amount
status
rd_id
users_id
rejected_amount
status
1
Skipper
1
1
1
20
1
2
2
5
1
1
Skipper
1
2
1
30
1
3
1
10
1
2
Private
1
3
2
100
1
1
2
15
1
2
Private
1
3
2
100
1
1
2
15
1
When you aggregate this data set, you add up amounts multifold. (Each value gets multiplied with the number of rows for the user in the other table).
Instead join the sums to the user:
select u.id, u.name, sh.supply_amount, rd.rejected_amount
from users u
left outer join
(
select users_id, sum(amount) as supply_amount
from supply_history
where status = 1
group by users_id
) sh on sh.users_id = u.id
left outer join
(
select users_id, sum(rejected_amount) as rejected_amount
from rejected_deal
where status = 1
group by users_id
) rd on rd.users_id = u.id
where u.status = 1
order by u.id;
I've turned your inner joins into outer joins for the case a user doesn't have entries in both tables. If you only want users that have data in both tables, then change this back to inner joins.
And if you want to enhance the query's readability, you may consider using WITH clauses: https://dev.mysql.com/doc/refman/8.0/en/with.html.

use Over for sum like this
SELECT users.id, users.name,
sum(supply_history.amount) OVER(PARTITION BY users.id, ORDER BY users.id) as supply_amount,
sum(rejected_deal.rejected_amount) OVER(PARTITION BY users.id, ORDER BY users.id) as rejected_amount
from users
inner join supply_history on supply_history.users_id=users.id
inner join rejected_deal on rejected_deal.users_id=users.id
where users.status='1' and rejected_deal.status='1' and supply_history.status='1'

try using sub queries to be something like that :
SELECT users.id, users.name,
(select sum(amount) from supply_history s where s.users_id = users.id and s.status = '1' ) as supply_amount ,
(select sum(rejected_amount) from rejected_deal r where r.users_id = users.id and r.status = '1' ) as rejected_amount
from users
where users.status='1';

Related

How can I retrieve Similar Orders In Mysql?

i need a query that should first look the oldest order which has status 0 (zero). and retrieves all the similar orders of that kind(matches exact total qty, itemSku and number of distinct items ordered).
***OrdersTable***
ID OrderNumber CustomerId Status created_at
1 123456 1 0 2018-01-01
2 234567 1 0 2018-01-02
3 345678 1 0 2018-01-03
4 456789 1 0 2018-01-04
***PurchasedProductsTable***
OrderId itemSku Qty
1 1000001 1
1 1000002 2
2 1000001 3
3 1000001 1
3 1000002 2
4 1000001 3
In the above table the query should first look at the oldest (created_at ASC) order (i.e with Id 1) having status 0 (in order table). and along with that order it should retrieves all the other orders that matches the same itemSku, qty and total distinct items count (in purchasedProducts table).
here order 1 and 3 matches the same itemSKu (1000001 and 1000002) and qty ( 1 and 2) and both have (2) distinct items count respectively so order 1 and 3 should be retrived at first.and when i marked order 1 and 3 as shipped (i.e chang status to 2).
and if i run query again it should retrive similar oders. now order 2 and 4 as order 2 and 4 are similar orders. (have same itemSkus (1000001, Qty (3) and distinct items count (1)).
please help thanks
You have to go trough your tables two times :)
Something like this :
SELECT DISTINCT O2.ID
FROM OrdersTable O1
INNER JOIN PurchasedProductsTable P1 ON O1.ID = P1.OrderId
INNER JOIN PurchasedProductsTable P2 ON P1.itemSku = P2.itemSku
AND P1.Qty = P2.Qty
INNER JOIN OrdersTable O2 ON O2.ID = P2.OrderId
WHERE O1.ID =
(SELECT ID FROM OrdersTable WHERE Status = 0
ORDER BY created_at ASC LIMIT 1)
AND (SELECT COUNT(*) FROM PurchasedProductsTable WHERE OrderId = O1.ID)
= (SELECT COUNT(*) FROM PurchasedProductsTable WHERE OrderId = O2.ID)
ORDER BY O2.ID ASC;
https://www.db-fiddle.com/f/65t9GgSfqMpzNVgnrJp2TR/2
You can get the earliest order via a limit and ordered by the date.
Then you can left join to get that order and any other order that at least has the same items.
Then once you have those order id's from the sub-query result, you can get the order details.
SELECT o.*
FROM
(
SELECT DISTINCT ord2.ID as OrderId
FROM
(
SELECT ID, CustomerId, Status
FROM OrdersTable
WHERE Status = 0
ORDER BY created_at
LIMIT 1
) AS ord1
JOIN PurchasedProductsTable AS pprod1
ON pprod1.OrderId = ord1.ID
LEFT JOIN OrdersTable ord2
ON ord2.CustomerId = ord1.CustomerId
AND ord2.Status = ord1.Status
LEFT JOIN PurchasedProductsTable pprod2
ON pprod2.OrderId = ord2.ID
AND pprod2.itemSku = pprod1.itemSku
AND pprod2.Qty = pprod1.Qty
GROUP BY ord1.CustomerId, ord1.ID, ord2.ID
HAVING COUNT(pprod1.itemSku) = COUNT(pprod2.itemSku)
) q
JOIN OrdersTable AS o ON o.ID = q.OrderId;
Test on RexTester here

Join tables and sort by 2 different columns

I have query that make a list of payments (payment table) and I would like to have it sorted by the date of the tasks (2 separate tables - quotedb and packaging) these payments are related to
My Query:
SELECT a.*
FROM payments AS a
LEFT JOIN quotedb AS b ON a.orderid = b.id
LEFT JOIN packaging AS p ON a.orderid = p.id
WHERE a.status='Pending' AND (b.moveday<'$today' OR p.datestamp<'$today')
ORDER BY b.moveday, p.datestamp
payments table example:
id payment orderid
-------------------
1 payment1 1
2 payment2 2
3 payment3 3
4 payment4 4
5 payment5 5
6 payment6 6
quotedb table example:
id moveday
-----------
1 05.07.18 > related to payments table id 1
2 08.07.18
3 10.07.18
packaging table example:
id datestamp
-----------
4 06.07.18 > related to payments table id 4
5 07.07.18
6 19.07.18
I join results from the tables, but I have a problem with sorting, query seem to print the "packaging" table results unsorted, and then results from "quotedb" sorted by moveday
I want these results to be sorted by (joined moveday and datestamp)
You can use UNION ALL to combine quotedb and packaging tables. and use grp make a number to make Order by sequence number
SELECT a.*
FROM payments a
LEFT JOIN
(
SELECT 1 grp,id,moveday AS day
FROM quotedb
UNION ALL
SELECT 2,id,datestamp
FROM packaging
) t on a.orderid = t.id
ORDER BY t.grp,t.day
sqlfiddle
SELECT a.*
FROM payments AS a
LEFT JOIN
(
SELECT orderno, moveday AS sortdate FROM quotedb
UNION ALL
SELECT orderno, datestamp AS sortdate FROM packaging
) t on a.orderno = t.orderno
WHERE a.status='Pending' AND sortdate<'$today'
ORDER BY sortdate

MySQL - Display null column from child table if all values are not distinct

I have the following tables, for example:
invoices
ID Name
1 A
2 B
3 C
4 D
5 E
transactions
ID Invoice_ID User_ID
1 1 10
2 1 10
3 1 10
4 2 30
5 3 20
6 3 40
7 2 30
8 2 30
9 4 40
10 3 50
Now I want to make a select that will pull the invoices and the user_id from the related transactions, but of course if I do that I won't get all the ids, since they may be distinct but there will be only one column for that. What I want to do is that if there are distinct User_ids, I will display a pre-defined text in the column instead of the actual result.
select invoices.id, invoices.name, transactions.user_id(if there are distinct user_ids -> return null)
from invoices
left join transactions on invoices.id = transactions.invoice_id
and then this would be the result
ID Name User_ID
1 A 10
2 B 30
3 C null
4 D 40
5 E null
Is this possible?
You can do the following :
select
invoices.id,
invoices.name,
IF (
(SELECT COUNT(DISTINCT user_id) FROM transactions WHERE transactions.invoice_id = invoices.id) = 1,
(SELECT MAX(user_id) FROM transactions WHERE transactions.invoice_id = invoices.id),
null
) AS user_id
from invoices
Or, alternatively, you can use the GROUP_CONCAT function to output a comma-separated list of users for each invoice. It is not exactly what you asked, but maybe in fact it will be more useful :
select
invoices.id,
invoices.name,
GROUP_CONCAT(DISTINCT transactions.user_id SEPARATOR ',') AS user_ids
from invoices
left join transactions on invoices.id = transactions.invoice_id
group by invoices.id
Try somethingh like:
select i.id, i.name, t.user_id
from invoices i left join
(
select invoice_ID, User_ID
from transactions
group by invoice_ID
having count(invoice_ID)=1
) t on i.id=t.invoice_id
SQL fiddle
You could list all the transactions that have multiple user ids, like this:
select invoices.id, invoices.name, null
from invoices
left join transactions on invoices.id = transactions.invoice_id having count(distinct transactions.user_id) > 1
Also, I think this CASE might suit your needs here:
select invoices.id, invoices.name,
case when count(distinct transactions.user_id) > 1 then null else transactions.user_id end
from invoices
left join transactions on invoices.id = transactions.invoice_id
group by invoices.id
although, I'm not sure this is syntactically correct

Mysql Group by counting issue

Database structure
Table 'applicants'
id org_id team_id
1 1 1
Table 'teams'
id name
1 Test
Table 'teams_members'
id team_id user_id
1 1 1
2 1 2
Table 'users_playeraccounts'
id user_id summoner_id rank_solo
1 1 1 5
2 1 2 8
3 2 3 7
select sum(rank_solo) as rank_sum,
max(rank_solo) as highest_rank,
count(tt.id) as members,
t.name,
o.team_id
from applicants o
join teams t on o.team_id = t.id
join teams_members tt on t.id = tt.team_id
join users_playeraccounts p on tt.user_id = p.user_id
where org_id = :org
group by team_id
This offcourse gives me a result like
rank_sum highest_rank members name team_id
20 8 3 Test 1
Is there a way for me to get both the count of members with their playeraccounts aka
If 1 user has 2 it'll be 2
And also a way for me to keep it as 1 so it literally just counts the rows found in teams_members neglecting the entries in users_playeraccounts?
I want to receive both 2 and 3 as a result of my query.
You want to count the distinct number of entries in tt.id, so you can do that like this:
SELECT ... COUNT(DISTINCT tt.id) AS distinct_members ...
Rather than giving you a count of every row that has a non-null tt.id, you'll get a count of the number of unique values.

Calculate average message quality per user in MySQL

Consider the following tables:
users messages
------------------- ----------------------
user_id avg_quality msg_id user_id quality
------------------- ----------------------
1 1 1 1
2 2 1 0
3 3 1 0
4 1 1
5 1 1
6 2 0
7 2 0
8 3 1
messages.quality is either 0 or 1. I need to calculate the average message quality per user and update users.avg_quality accordingly. So the desired output would be modified users table like so:
users
-------------------
user_id avg_quality <-- DECIMAL (8,2)
-------------------
1 0.60 <-- (3x1 + 2x0) / 5
2 0.00 <-- (2x0) / 2
3 1.00 <-- (1x1) / 1
I've begun my query like this, I know the syntax is incorrect but have no better idea. Do you?
UPDATE messages m, users u
SET avg_quality = (SELECT COUNT(m.msg_id) / SUM(m.quality))
WHERE m.user_id = u.user_id
This should work:
UPDATE users u
INNER JOIN (SELECT a.user_id, AVG(quality) avg_quality
FROM messages a
INNER JOIN users b
ON a.user_id = b.user_id
GROUP BY a.user_id
) tmp
ON u.user_id = tmp.user_id
SET u.avg_quality = tmp.avg_quality;
See the average function:
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_avg
Your select should be something like this:
select user_id, AVG(quality) from messages group by user_id
If you begin with an empty users table you could run a query like this one to update it all:
insert into users (user_id, avg_quality)
select m.user_id, coalesce(AVG(m.quality),0) from messages m group by m.user_id
If you need continuous results Luc's proposal will work for you:
update users u left join (
select m.user_id, AVG(m.quality) as average from messages m group by m.user_id
) as average_result_t on u.user_id = average_result_t.user_id
set u.average = coalesce(average_result_t.average,0)