MySQL nearest date without duplicated data - mysql

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

Related

MySQL Inner join and sum two columns

I have the following tables
TABLE: appointments
ID | PRICE | PAID
48 | 100 | 180
TABLE: appointments_products
ID | APPOINTMENT_ID | PRODUCT_ID | TOTAL
10 | 48 | 1 | 30
11 | 48 | 9 | 30
12 | 48 | 6 | 30
I Would like to somehow run a MySQL query that will:
a) join the two tables, SUM the "TOTAL" of appointments_products for each appointment_id and if the "PAID" is not equal of the PRICE (from appointments table) + TOTAL (from appointments_products table) then to show it.
This is what I have done so far:
select a.*, b.appointment_id as AppId, b.total as ProdTotal
from appointments a
INNER JOIN appointments_products b ON a.id = b.appointment_id
But this query does not sum the total for each appointment_id
select a.ID,a.PRICE,a.PAID,a.id as AppId,
sum(b.total) as ProdTotal
from appointments a
INNER JOIN appointments_products b ON a.id = b.appointment_id
group by a.ID,a.PRICE,a.PAID;
Use where to check if price is equal to paid and the use group by to group with appointment_id.
select b.Appointment_Id, a.price, a.PAID, a.id, sum(b.total) AS TotalProd FROM appointments_products AS b inner join appointments as a On Appointment_Id = a.Id group by Appointment_Id, a.Price , a.PAID , a.id HAVING a.PAID != (a.Price + sum(b.Total))

Mysql left join style sum()

I believe the answer is already there at stackoverflow but I cannot find the right keywords. So please help.
Table sales looks like this:
state | sales-representative | product | sales
NY | Mike B. | prod-A | 90
FL | David J. | prod-B | 120
FL | Mike B. | prod-A | 15
I need to get the total sales by such sales representative. Expected results for Mike B. look at this:
state | product | sales
NY | prod-A | 90
FL | prod-A | 15
NY | prod-B | 0 <--How can I get this record as well?
FL | prod-B | 0
A regular sum query returns the first 2 records. How can I get the last 2 records as well?
select state, product, sum(sales)
from sales
where sales-representative = 'Mike B.'
group by state, product
SELECT ss.state, sp.product, SUM(sr.sales)
FROM (SELECT DISTINCT state FROM sales) AS ss
CROSS JOIN (SELECT DISTINCT product FROM sales) AS sp
LEFT JOIN sales AS r
AS sr ON ss.state = s.state
AND sp.product = s.product
AND r.`sales-representative` = 'Mike B.'
GROUP BY ss.state, sp.product;
The cross join gets you every combination of state and product, and the left join gets you the specified representative's associated sales.
You can use a left join on subquery for stated and product
select t.state, t.product, sum(sales)
from table1
left join (
select t1.state, t2.product
from t1
cross join (
select product
from t1
) t2
) t on t.state = table1.state and t.product = table1.product
grooup by t.state, t.product

Mysql select from subquery is not working

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

find customers and their first payment

I have 3 tables.
Accounts :
id
1
2
3
Customers :
id | account | email
76276 | 1 | test1#mail.com
143158 | 2 | test2#mail.com
143159 | 3 | test3#mail.com
Payments :
id | customer | date
285041 | 76276 | 2014-12-01 00:13:41
285042 | 76276 | 2014-12-01 00:15:55
285043 | 143158 | 2014-12-01 00:18:52
285044 | 143159 | 2014-12-02 12:21:47
I want to get all the accounts whose customer's first payment is between 2014-12-01 00:00:00 and 2014-12-01 23:59:59
I tried
SELECT a.id
FROM account a
JOIN customer c ON c.account = a.id
JOIN payment p ON p.id = (
SELECT p.id
FROM payment p
WHERE p.customer = c.id
AND p.date BETWEEN '2014-12-01 00:00:00' AND '2014-12-01 00:59:59'
ORDER BY date ASC
LIMIT 1
)
But this query runs for ages.. and i'm pretty sure this is not how I should join payment table. Some help would be appreciated.
Find the first payment date by using min(). Then just get the appropriate information:
select account
from customers c join
payments p
on p.customer = c.id
group by account
having min(p.date) >= '2014-12-01' and min(p.date) < '2014-12-02';
this query might be helpful.
SELECT a.id FROM accounts a
INNER JOIN Customers c ON c.account=a.id
INNER JOIN payments p ON p.customer=c.id
WHERE p.date BETWEEN '2014-12-01 00:00:00' AND '2014-12-01 23:59:59';

MySQL total sum based on previous select by date

I have a not-very-normalized MySQL table with the following values:
customer_ID | date | score | sale
As I am scoring the "purchase experience", I would like to check the total sale value for each customer based on his last score. I do not mind about previous scores, just about last one.
Let's say I have:
customer_ID | date | score | sale
a | yesterday | 3 | 10
a | today | 6 | 35
b | today | 10 | 20
c | yesterday | 4 | 5
The result for customers purchases with score > 5 should be:
num of customers | total sale values
2 | 65
Any idea of how to create such query?
Edit:
What I want to know is how much has a customer spent in purchases in total, but just on customers whose last score was bigger than 5.
SELECT COUNT(DISTINCT aa.customer_ID) `num of customers`,
SUM(aa.sale) `total sale values `
FROM table1 aa
INNER JOIN
(
SELECT a.customer_ID
FROM table1 a
INNER JOIN
(
SELECT customer_ID, max(date) max_date
FROM table1
GROUP BY customer_ID
) b ON a.customer_ID = b.customer_ID AND
a.date = b.max_date AND a.score > 5
) final ON aa.customer_ID = final.customer_ID
SQLFiddle Demo
much more simplified,
SELECT COUNT(DISTINCT c.customer_ID) `num of customers`,
SUM(c.sale) `total sale values `
FROM table1 a
INNER JOIN
(
SELECT customer_ID, max(date) max_date
FROM table1
GROUP BY customer_ID
) b ON a.customer_ID = b.customer_ID AND
a.date = b.max_date AND a.score > 5
INNER JOIN table1 c
ON a.customer_ID = c.customer_ID
SQLFiddle Demo