Mysql select from subquery is not working - mysql

I want to create report of total number of orders and total earning that are belong to each users.
SELECT w.id, CONCAT_WS(' ', w.fname, w.sname) AS full_name,
te.total_earnings, te.assigned_jobs
FROM users AS w
LEFT JOIN (
SELECT wr.user_id,
COUNT(o.order_id) AS assigned_jobs,
SUM(o.fee) AS total_earnings
FROM (
SELECT DISTINCT user_id, order_id, withdrawn
FROM work_records
) AS wr
LEFT JOIN orders o ON o.order_id = wr.order_id
WHERE wr.withdrawn IS NULL
AND o.verified != 'rejected'
) AS te ON te.user_id = w.id
WHERE w.status = 1
orders work_records
___________________ _________________________________
| order ID | fee | | id | order_id | fee | user_id |
------------------- ---------------------------------
| 334 | 425 | | 1 | 334 | 50 | 6 |
| 2 | 334 | 50 | 6 |
This query works on single user id. But it doesn't work if I want to get report of all users.
Any advise thanks?

Here is the answer for others. How ever the query is slower. But if you have faster query would greate to share.
SELECT w.id, CONCAT_WS(' ', w.fname, w.sname) AS full_name,
te.total_earnings, te.assigned_jobs
FROM users AS w
LEFT JOIN (
SELECT w.id,
SUM(work.earnings) AS total_earnings,
COUNT(work.order_id) AS assigned_jobs
FROM users AS w
LEFT JOIN (
SELECT wr.order_id, wr.writer_id, o.fee AS earnings
FROM work_records wr
LEFT JOIN orders o ON o.order_id = wr.order_id
WHERE wr.withdrawn IS NULL
AND o.verified = 'verified'
GROUP BY wr.order_id
) work ON work.writer_id = w.id
GROUP BY work.writer_id
) te ON te.id = w.id

Related

MySQL nearest date without duplicated data

So I need to display all my customers and with the associated booking number (null if there is no booking) without duplicated custome. If the customer has lot of bookings I need to display only the nearest booking date. I don't understand why my query doesn't work.
Here is what is did : http://sqlfiddle.com/#!9/df0455/19
SELECT c.name, x.number, x.start_date
FROM customer c
LEFT JOIN
(SELECT b.customer_id, b.number, b.start_date
FROM booking b
INNER JOIN (
SELECT customer_id, MIN(ABS(TIME_TO_SEC(TIMEDIFF(NOW(), start_date)))) as mindiff
FROM booking
GROUP BY customer_id
) nearest ON b.customer_id = nearest.customer_id AND ABS(TIME_TO_SEC(TIMEDIFF(NOW(), start_date))) = mindiff
) AS x ON c.id = x.customer_id
Actually Paul is displayed three times and what is need is to display Paul just once with the nearest booking number who is booking-1 2019-11-05 21:45:00
I hope you can help me
You can filter with a row-limiting correlated subquery:
select c.name, b.number, b.start_date
from customer c
inner join booking b on b.customer_id = c.id
where b.start_date = (
select b1.start_date
from booking b1
where b1.customer_id = b.customer_id
order by abs(timestampdiff(second, now(), b1.start_date))
limit 1
)
In your DB Fiddle, this produces:
name number start_date
Paul booking-1 2019-11-05T21:45:00Z
John booking-3 2019-09-27T21:45:00Z
Morgan booking-5 2019-09-27T21:45:00Z
If you wanted to also display customers without bookings, then you would left join and move the filtering to the on clause of the join:
select c.name, b.number, b.start_date
from customer c
left join booking b
on b.customer_id = c.id
and b.start_date = (
select b1.start_date
from booking b1
where b1.customer_id = b.customer_id
order by abs(timestampdiff(second, now(), b1.start_date))
limit 1
)
You can use NOT EXISTS to get the nearest booking and join to customer:
SELECT c.id, c.name, t.number, t.start_date
FROM customer c
LEFT JOIN (
SELECT b.* FROM booking b
WHERE NOT EXISTS (
SELECT 1 FROM booking
WHERE customer_id = b.customer_id
AND ABS(TIMESTAMPDIFF(SECOND, NOW(), start_date)) < ABS(TIMESTAMPDIFF(SECOND, NOW(), b.start_date))
)
) t ON t.customer_id = c.id
See the demo.
Results:
| id | name | number | start_date |
| --- | ------ | --------- | ------------------- |
| 1 | Paul | booking-1 | 2019-11-05 21:45:00 |
| 2 | John | booking-3 | 2019-09-27 21:45:00 |
| 3 | Morgan | booking-5 | 2019-09-27 21:45:00 |
| 4 | Jane | | |
| 5 | Mike | | |

SQL selecting most recent row inside join

I have 2 tables companies and invoices
I want to select all companies with their most recent invoice price.
I don't seem to get it working.
This is what I tried:
SELECT *
FROM companies H INNER JOIN
invoices V
ON H.company_id = V.BC_ID
WHERE V.ISCOMMISSIE = 0 AND
V.DATE = (SELECT MAX(v2.DATE) FROM invoices v2 WHERE v2.BC_ID = V.BC_ID AND v2.ISCOMMISSIE = 0);
But the query loads very long and I don't know why.
The structure looks like this:
companies
company_id | company_name |
1 | company 1 |
2 | company 2 |
invoices
invoice_id | BC_ID | DATE | ISCOMMISSIE | price |
1 | 2 | 2020-01-01 | 0 | 340,40 |
2 | 1 | 2020-01-11 | 0 | 240,40 |
3 | 1 | 2020-01-08 | 0 | 250,30 |
4 | 2 | 2020-01-18 | 0 | 150,30 |
5 | 2 | 2020-01-19 | 1 | 150,30 |
The BC_ID is the same as the company_id and ISCOMMISSIE should be 0.
I want to select the most recent date.
Does someone have an idea on how to do this and also make the query as fast as possible?
http://sqlfiddle.com/#!9/2fc3a/1
Try:
SELECT H.*, V.*
FROM companies H
INNER JOIN invoices V ON H.company_id = V.BC_ID
INNER JOIN ( SELECT v2.BC_ID, MAX(v2.DATE) DATE
FROM invoices v2
WHERE v2.ISCOMMISSIE = 0
GROUP BY v2.BC_ID ) v3 ON v.BC_ID = v3.BC_ID
AND v.DATE = v3.DATE
AND V.ISCOMMISSIE = 0
And the index invoices (ISCOMMISSIE, BC_ID, DATE) may help...
Your query is fine:
SELECT *
FROM companies H INNER JOIN
invoices V
ON H.company_id = V.BC_ID
WHERE V.ISCOMMISSIE = 0 AND
V.DATE = (SELECT MAX(v2.DATE)
FROM invoices v2
WHERE v2.BC_ID = V.BC_ID AND
v2.ISCOMMISSIE = 0
);
For performance, you want an index on invoices(BC_ID, ISCOMMISSIE, DATE).
A good alternative is to use window functions:
SELECT *
FROM companies H INNER JOIN
(SELECT V.*,
ROW_NUMBER() OVER (PARTITION BY BC_ID ORDER BY DATE DESC) as seqnum
FROM invoices V
WHERE V.ISCOMMISSIE = 0
) V
ON H.company_id = V.BC_ID
WHERE seqnum = 1;
Depending on columns you need, you might not need to join with companies table. Also it is not needed to test for iscommissie = 0 two times, you can just test it one time in the subquery before joining.
See the query below :
SELECT i.*
FROM invoices i
JOIN (
SELECT i.bc_id, MAX(date) AS max_date
FROM invoices i
WHERE iscommissie = 0
GROUP BY i.bc_id
) i_temp ON i.bc_id = i_temp.bc_id AND i.date = i_temp.max_date
FIND A DEMO HERE
Another way to get the expected output:
select * from companies A join (
select * from invoices where (BC_ID,DATE) in(
select BC_ID as BC_ID, MAX(DATE) DATE from invoices where ISCOMMISSIE = 0 group by
BC_ID
))B on A.company_id=B.BC_ID;

SUM two columns of two different tables in one result based group by id of another table

I have 3 tables:
Users:
id | account_name
-------------------|----------------------|
18 | panic |
Deposits:
id | user_id | amount
-------------------|---------------------------|
1 | 18 | 100
2 | 18 | 100
Withdrawals:
id | user_id | amount
------------------------|--------------------------------|
1 | 18 | 200
2 | 18 | 200
and i'm trying to get a result like:
id | totalDeposits | totalWithdraws
------------------------|---------------------------|
18 | 200 | 400
Now when i try to get the totals for some reason they are cross adding themselves up, of course if there are no rows it should return 0.
SELECT t0.id,IFNULL(SUM(t1.amount),0) AS totalWithdrawals,
IFNULL(SUM(t2.amount),0) AS totalDeposits
FROM users t0
LEFT OUTER JOIN withdrawals t1 ON (t0.id = t1.user_id)
LEFT OUTER JOIN deposits t2 ON (t0.id = t2.user_id)
GROUP BY t0.id
Any idea how to do this cross join or where am i summing them wrong?
Try this-
SELECT A.id,
(SELECT SUM(amount) FROM Deposits WHERE user_id = A.id) totalDeposits,
(SELECT SUM(amount) FROM Withdrawals WHERE user_id = A.id) totalWithdraws
FROM users A
WHERE A.id = 18 -- WHERE can be removed to get all users details
You can try something along the lines of
SELECT u.id,
COALESCE(d.amount, 0) totalDeposits,
COALESCE(w.amount, 0) totalWithdrawals
FROM users u
LEFT JOIN (
SELECT user_id, SUM(amount) amount
FROM deposits
GROUP BY user_id
) d ON u.id = d.user_id
LEFT JOIN (
SELECT user_id, SUM(amount) amount
FROM withdrawals
GROUP BY user_id
) w ON u.id = w.user_id
SQLFiddle
Result:
| id | totalDeposits | totalWithdrawals |
|----|---------------|------------------|
| 18 | 200 | 400 |
The problem is that you are generating a Cartesian product. One solution is to aggregate first. Another method is to use UNION ALL and GROUP BY. I would structure this as:
SELECT u.id,
SUM(deposit) as deposits,
SUM(withdrawal) as withdrawal
FROM users u LEFT JOIN
((SELECT d.user_id, d.amount as deposit, 0 as withdrawal
FROM deposits d
) UNION ALL
(SELECT w.user_id, 0, w.amount
FROM withdrawals w
)
) dw
ON u.id = dw.user_id
GROUP BY u.id;

sql query combine two queries into one with empty rows

This is my sql table structure:
Table1: details
|--id--|--id_user--|--price--|
| 1 | 1 | 10 |
| 2 | 2 | 15 |
| 3 | 1 | 25 |
| 4 | 3 | 30 |
| 5 | 3 | 7 |
------------------------------
Table2: users
|--id--|--id_country--|
| 1 | 1 |
| 2 | 2 |
| 3 | 0 |
-----------------------
Table3: country
|--id--|--country--|
| 1 | France |
| 2 | Italy |
--------------------
What I need is to get the SUM of price by country:
SELECT c.country, SUM(d.price) AS price
FROM details d
INNER JOIN users u ON u.id = d.id_user
INNER JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country
I get this:
|--country--|--price--|
| France | 35 |
| Italy | 15 |
-----------------------
BUT I'd need to get this:
|--country--|--price--|
| France | 35 |
| Italy | 15 |
| Undefined | 37 |
-----------------------
where undefined would be if id_country=0. (I can't add to country table the id=0 or id=undefined, it will messed up other things). Right now I'm achieving this by two separate queries, the second one is:
SELECT SUM(d.price) as price
FROM details d
INNER JOIN users u ON u.id = d.id_user AND u.id_country=0
GROUP BY u.id_country
I'm thinking if... is it possible to do this in one query?
You need to use left join in this case:
SELECT c.country, SUM(d.price) AS price
FROM details d
LEFT JOIN users u ON u.id = d.id_user
LEFT JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country
If you use INNER JOIN, you will only get results that exists in both tables.
To replace NULL with Undefined use:
SELECT IFNULL(c.country,'Undefined') AS Country, SUM(d.price) AS price
FROM details d
LEFT JOIN users u ON u.id = d.id_user
LEFT JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country
One way to sort to get Undefined last is to add a Sortfield
SELECT A.Country,A.Price FROM (
SELECT IFNULL(c.country,'Undefined') AS Country, SUM(d.price) AS price, IFNULL(c.Country,'ZZZZZZZZ') AS Sort
FROM details d
LEFT JOIN users u ON u.id = d.id_user
LEFT JOIN country c ON c.id = u.id_country
GROUP BY c.country
) A
ORDER BY A.Sort
Edit: ORDER BY suggested in comments
SELECT IFNULL(c.country,'Undefined') AS Country, SUM(d.price) AS price
FROM details d
LEFT JOIN users u ON u.id = d.id_user
LEFT JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country IS NULL, c.country
Try below query.
SELECT
CASE
WHEN c.country is NULL THEN 'Undefined'
ELSE c.country
END as country
, SUM(d.price) AS price
FROM users u
left JOIN details d ON u.id = d.id_user
left JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country
For Demo :
SqlfiddlE Demo :
Please let us know if you have any que.

Joining Max Row where Col

I have this query:
SELECT
`shift`.`uid`,
`shift`.`activity`,
`users`.`fname`,
`users`.`lname`
FROM `shift`, `users`
WHERE `shift`.`uid` = `users`.`id`
It works fine just like that, but I need to add a new column from another table and order by it.
times :
| uid | User | time |
+++++++++++++++++++++
| 3 | bob | 1231 |
| 3 | bob | 1291 |
| 4 | ned | 1651 |
| 5 | ted | 5679 |
| 6 | joe | 7665 |
| 6 | joe | 7864 |
How can I include the maximum time from the time table for each user (WHERE times.uid = shift.uid) and then order by that column?
Trouble is, all the other tables have one row per user but the time table has multiple and I can't figure out the correct combination of joins and group by.
You could join on an aggregate query:
SELECT `shift`.`uid`,
`shift`.`activity`,
`users`.`fname`,
`users`.`lname` ,
t.max_time
FROM `shift`
JOIN `users` ON `shift`.`uid` = `users`.`id`
JOIN (SELECT `uid`, MAX(`time`) AS max_time
FROM `times`
GROUP BY `uid`) t ON shift.uid = t.uid
ORDER BY t.max_time
A pretty simple way to approach this is using a correlated subquery:
SELECT s.`uid`, s.`activity`, u.`fname`, u.`lname`,
(SELECT MAX(tt.time)
FROM timetable tt
WHERE tt.uid = u.id
) as maxtime
FROM `shift` s JOIN
`users` u
ON s.`uid` = u.`id`;
The advantage of this approach is performance. With an index on timetable(uid, time), this should work better than doing an aggregation at the outer level (because the query will take advantage of the index).
SELECT s.uid,
s.activity,
u.fname,
u.lname,
MAX(t.time) as maxtime
FROM shift s,
INNER JOIN users u ON u.id = s.uid
INNER JOIN times t ON t.uid = u.id
GROUP BY s.uid,
s.activity,
u.fname,
u.lname
ORDER BY maxtime