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;
Related
I am working with a local mysql server and I have two tables.
Table A:
ID | Name
1 Joe
2 Bob
3 John
Table B:
ID | DATE | Name_ID | Point
1 2010-01-01 1 1
2 2011-01-01 1 1
3 2013-01-01 1 -1
4 2010-01-01 2 -1
5 2012-01-01 2 -1
6 2013-01-01 2 -1
7 2014-01-01 2 1
For each ID in Table A, I am trying to get the
max date and
a running count of their points. (dates and points being in Table B).
Wanted Result:
Name | Latest_Date | Point_total
John NULL NULL
Joe 2013-01-01 1
Bob 2014-01-01 -2
So far I Have this Query:
SELECT DISTINCT a.Name, b.Date, SUM(b.point) OVER (PARTITION BY b.Name_ID) Point_total FROM
TableA a
LEFT OUTER JOIN TableB b
ON a.ID = b.Name_ID
INNER JOIN (
SELECT b.Name_ID, MAX(b.Date) Latest_Date
FROM TableB b GROUP BY b.Name_ID ) m
ON (m.Latest_Date = b.Date or (b.Date is NULL)) AND
(b.Name_ID = m.Name_ID OR (b.Name_ID is NULL))
ORDER BY Name_ID;
The resulting table does output the correct max date, but the running total only takes into account the point of the entry with the max date.
What am I missing here?
This just looks like aggregation:
SELECT a.Name, MAX(b.Date), SUM(b.point) as Point_total
FROM TableA a LEFT OUTER JOIN
TableB b
ON a.ID = b.Name_ID
GROUP BY a.name
That's an aggregation query:
select a.name, max(b.date) latest_date, sum(b.point) point_total
from a
left join b on b.name_id = a.id
group by a.id, a.name
SELECT A.Name, MAX(B.Date) as Max_Date, SUM(b.point) as Total_Points
FROM TableA as A
LEFT JOIN TableB as B
ON A.ID = B.Name_ID
GROUP BY A.name
i have 2 tables A and B like below
Table A
+---------+--------+
| query | status |
+---------+--------+
| number1 | solved |
+---------+--------+
table B
+----+---------+---------+
| id | query | status |
+----+---------+---------+
| 1 | number1 | started |
| 2 | number1 | working |
| 3 | number1 | solved |
+----+---------+---------+
how do i check whether the latest status of table B is equal to status of Table A
i have tried first getting latest status for table 2 but i am unable to get so
select number ,max(status),max(id)
from Table B
group by number
order by max(id) asc
Here is a simple solution relying on sorting by ID and getting only first row in a sub-query. It will only returns rows that has a match between the two tables.
SELECT a.*
FROM tableA a
JOIN (SELECT query, status
FROM tableB
ORDER BY ID desc
LIMIT 1) b ON a.query = b.query AND a.status = b.status
use join and subquery
select a.* from
tablea a join
(
select * from table_name t1
where id =( select max(id) from table_name t2 where t1.query=t2.query)
) b join on a.query=b.query and a.status=b.status
select *
from A a
inner join B b on b.query = a.query
inner join
(select max(id) id, query
from B
group by query) gq on gq.id = b.id and gq.query = b.query
where a.status = b.status
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))
I am using MySQL. Here is my schema:
Table b
Column Name | Type | Primary key
id | int | Yes
seq | int | Yes
amt | int
Dummy data
id | seq | amt
1 | 1 | 4000
1 | 2 | 3000
1 | 3 | 2000
1 | 4 | 5000
2 | 1 | 4000
2 | 2 | 3000
3 | 1 | 2000
3 | 2 | 5000
I want to select the record with equivalent id and max value of seq.
HERE is my SQL
SELECT b.id, b.seq, b.amt
FROM b
WHERE b.id = 1
AND b.seq =
(SELECT max(b.seq) FROM b WHERE b.id = 1)
But I wonder if there is more elegant way of achieving what I want.
For example,
SELECT b.id, b.seq, b.amt
FROM b
WHERE b.id = 1
HAVING b.seq = max(b.seq)
But it doesn't work as expected. It returns 0 rows.
The HAVING clause is to be used with the GROUP BY clause, which is missing in your query. To add a GROUP BY clause to your query, we'll have to include all the fields in the query that don't have an aggregate function, so everything other than seq:
SELECT b.id, b.seq, b.amt
FROM b
WHERE b.id = 1
GROUP BY b.id, b.amt
HAVING b.seq = MAX(b.seq)
Now that will obviously not give your the correct results, because you only want to group by id and not amt. Another problem is that you cannot use the fields that are not in the GROUP BY clause in either the SELECT or HAVING clauses, so you cannot use the seq in those two places, and the query above will give you an error.
If your goal is to get the record for id = 1, then your first query is OK, or better to use the query in juergen's answer. But if your real goal is to select one record for each group, then you can do it like this:
SELECT b.id, b.seq, b.amt
FROM b
INNER JOIN (SELECT id, MAX(seq)
FROM b
GROUP BY id) bb ON bb.id = b.id AND bb.seq = b.seq
The result will be:
id | seq | amt
1 | 4 | 5000
2 | 2 | 3000
3 | 2 | 5000
Order the data and take only the first record
SELECT b.id, b.seq, b.amt
FROM b
WHERE b.id = 1
ORDER BY seq desc
limit 1
Given your simple example, how about this:
SELECT b.id, b.seq, b.amt
FROM b
WHERE b.id = 1 ORDER BY b.seq DESC limit 1;
SQL HAVING Clause
HAVING filters records that work on summarized GROUP BY results.
HAVING applies to summarized group records, whereas WHERE applies to individual records.
Only the groups that meet the HAVING criteria will be returned.
HAVING requires that a GROUP BY clause is present.
WHERE and HAVING can be in the same query.
I have a simple MySQL table:
| id | sid | date |
+--------+---------+------------+
| 1 | 1 | 2013-12-01 |
+--------+---------+------------+
| 3 | 2 | 2013-12-17 |
+--------+---------+------------+
| 4 | 1 | 2013-12-17 |
+--------+---------+------------+
| 5 | 1 | 2013-12-18 |
+--------+---------+------------+
I need group this table by sid field and get records with max id for each sid with correct date. I try below code:
SELECT MAX(id), date FROM my_table GROUP BY sid
But the date field is incorrect, for example I get date 2013-12-01 with id 5 as a result.
What am I doing wrong ?
The way standard SQL is defined (at least up to ansi 1992, others will correct me), any field of your SELECT clause must be included in your group by condition. [Mysql allows you to not do so, but that is why it is confusing: the results are not as you expect]
Your query should then be:
SELECT MAX(id), date FROM my_table GROUP BY sid, date
But in this case, clearly this is not what you want. Your requirement is to get the date corresponding to the MAX(id) for each sId.
You have to isolate each part of the algorithm in different queries.
1 - get the max id for each sid:
SELECT MAX(id) AS id, sid FROM my_table GROUP BY sid
2 - get the date corresponding to the max of ids for each sid:
SELECT date FROM my_table WHERE sid = X AND id = Y
3 - join these 2 queries using an INNER JOIN or more shortly, a JOIN:
SELECT m.sid, m.id, m.date
FROM my_table m
JOIN (SELECT MAX(id) AS id, sid FROM my_table GROUP BY sid) t
ON t.sid = m.sid AND m.id = t.id
You need a join:
SELECT a.id, a.date
FROM foo a
INNER JOIN (SELECT MAX(id) as max_id FROM foo GROUP BY sid) b ON a.id = b.max_id