MySQL Inner join and sum two columns - mysql

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

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

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;

Selecting Most Recent Date Relative to Another Table

Just stumped on syntax for this...
I have Two tables in mysql & I need to fetch records from Table A when following criteria are met:
1) Name in Table A matches the name in Table B
AND
2) The price for the most recent day in Table B is less than the record in Table A
So...running the query on example tables below would fetch me these two records:
03-17-2019 Bob 8
03-20-2019 John 10
Essentially, I need to evaluate each row in Table A, check the matching name in Table B that has the most recent date relative to the record under evaluation in Table A, and then determine if the price in Table A is greater than the price for the most recent matching name in Table B. After that, I need to calculate the difference between the prices. So, in the two records above, the difference would be 2 and 4
Table A
Date | Name | Price
03-08-2019 Bob 6
03-25-2019 Bob 2
03-17-2019 Bob 8
03-20-2019 John 10
Table B
Date | Name | Price
03-16-2019 Bob 4
03-28-2019 Bob 9
03-02-2019 Bob 12
03-10-2019 John 6
Thank you for the help!
Join twice the tables, once to get the min date difference and then to get the row with the min date difference:
select a.*
from tablea a
inner join tableb b on b.name = a.name
inner join (
select a.name, min(abs(datediff(b.date, a.date))) mindatediff
from tablea a inner join tableb b
on b.name = a.name
group by a.name
) g on g.name = a.name and abs(datediff(b.date, a.date)) = g.mindatediff
See the demo.
or:
select a.*
from tablea a inner join tableb b
on b.name = a.name
where abs(datediff(b.date, a.date)) = (
select min(abs(datediff(x.date, y.date)))
from tablea x inner join tableb y
where x.name = a.name and y.name = b.name
)
See the demo.
Results:
| date | name | price |
| ---------- | ---- | ----- |
| 2019-03-17 | Bob | 8 |
| 2019-03-20 | John | 10 |
In MySQL 8+, you would use window functions
select ab.*, (price - b_price)
from (select a.*, b.price as b_price,
row_number() over (partition by a.name order by datediff(b.date, a.date) as seqnum
from a join
b
on a.name = b.name and
a.date >= b.date
) ab
where seqnum = 1;

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