Join Nearest Date - mysql

I have read through a ton of responses on here but nothing is working quite as well as I would like. I currently have a working query that includes 2 sub queries, the problem is that it takes about 10 seconds to execute. I was wondering if there is any way to make this go quicker, maybe with a join. I just can't seem to get my head out of the box it is in. Please let me know your thoughts.
Here is the working query:
Select concat(a.emp_firstname, ' ', a.emp_lastname) as names
, if(if (a.emp_gender = 1, 'Male', a.emp_gender)=2, 'Female',
if (a.emp_gender = 1, 'Male', a.emp_gender)) as emp_gender
, c.name
, a.emp_work_telephone
, a.emp_hm_telephone, a.emp_work_email
, a.custom7, a.employee_id
, a.city_code, a.provin_code, d.name as status,
(SELECT cast(concat(DATE_FORMAT(e.app_datetime, '%H:%i'), ' ', e.app_facility) as char(100))
FROM li_appointments.li_appointments as e where e.terp_id = a.employee_id
and e.app_datetime <= str_to_date('06/26/13 at 3:20 PM', '%m/%d/%Y at %h:%i %p')
and date(e.app_datetime) = date(str_to_date('06/26/13 at 3:20 PM', '%m/%d/%Y at %h:%i %p'))
order by e.app_datetime desc limit 1) as prevapp,
(SELECT cast(concat(DATE_FORMAT(e.app_datetime, '%H:%i'), ' ', e.app_facility) as char(100))
FROM li_appointments.li_appointments as e
where e.terp_id = a.employee_id
and e.app_datetime > str_to_date('06/26/13 at 3:20 PM', '%m/%d/%Y at %h:%i %p')
and date(e.app_datetime) = date(str_to_date('06/26/13 at 3:20 PM', '%m/%d/%Y at %h:%i %p'))
order by e.app_datetime desc limit 1) as nextapp
from hs_hr_employee as a
Join hs_hr_emp_skill as b on a.emp_number = b.emp_number
Join ohrm_skill as c on b.skill_id = c.id
Join orangehrm_li.ohrm_employment_status as d on a.emp_status = d.id
where c.name like '%Arabic%'
and d.name = 'Active' order by rand();
EXPLAIN results:
+----+--------------------+-------+--------+---------------------+------------+---------+---------------------------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+---------------------+------------+---------+---------------------------+-------+----------------------------------------------+
| 1 | PRIMARY | d | ALL | PRIMARY | | | | 10 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | a | ref | PRIMARY,emp_status | emp_status | 5 | orangehrm_li.d.id | 48 | Using where |
| 1 | PRIMARY | b | ref | emp_number,skill_id | emp_number | 4 | orangehrm_li.a.emp_number | 1 | |
| 1 | PRIMARY | c | eq_ref | PRIMARY | PRIMARY | 4 | orangehrm_li.b.skill_id | 1 | Using where |
| 3 | DEPENDENT SUBQUERY | e | ALL | | | | | 28165 | Using where; Using filesort |
| 2 | DEPENDENT SUBQUERY | e | ALL | | | | | 28165 | Using where; Using filesort |
+----+--------------------+-------+--------+---------------------+------------+---------+---------------------------+-------+----------------------------------------------+

Your tables appear small enough to do as I have done here. First, the inner-most query starts with all employees and did TWO left-joins immediately to the appointment table... By getting a MAX() of the appointments less than the date in question gets the "Previous Appointment", and getting the MIN() appointment AFTER the date in question gets the "Next Appointment". So now, for a single person, I have both their ID and possible previous and next appointments based on their specific times.
Now, I take that result and re-join first to the appointment tables (left joined again) but this time based on same person (Terp_ID) AND their respective Previous and Next appointments date/time. This would only be a problem if you had multiple entries with the exact same date/time for a single person, and that would just result in multiple records.
So now, I have each person with the specifics of previous and next appointments available.
The rest is simple joining to the other tables to get only employee status of "Active", and the skill set of "Arabic" criteria (which I have at their respective JOIN criteria), otherwise you could just move these to a WHERE clause.
As for the "Date/Time" basis of the query, I used #variable once so it could be used against both left-joins to appointments. Finally, I grabbed the respective fields you wanted. This SHOULD work, yet without your data, might need some tweaking.
SELECT
EmpPrevNext.Employee_ID,
EmpPrevNext.PrevApnt,
EmpPrevNext.NextApnt,
concat(Emp2.emp_firstname, ' ', Emp2.emp_lastname) as names,
if ( Emp2.emp_gender = 1, 'Male', 'Female' ) as emp_gender,
Emp2.emp_work_telephone,
Emp2.emp_hm_telephone,
Emp2.emp_work_email,
Emp2.custom7,
Emp2.city_code,
Emp2.provin_code,
cast( concat( DATE_FORMAT(PriorApp2.app_datetime, '%H:%i'), ' ', PriorApp2.app_facility) as char(100))
as PriorAppointment,
cast( concat( DATE_FORMAT(NextApp2.app_datetime, '%H:%i'), ' ', NextApp2.app_facility) as char(100))
as NextAppointment,
EStat.`name` as EmployeeStatus,
Skill.`name` as SkillName
FROM
( SELECT
Emp.Employee_ID,
MAX( PriorApp.app_DateTime ) as PrevApnt,
MIN( NextApp.app_DateTime ) as NextApnt
from
( select #DateBasis := '06/26/13 at 3:20 PM' ) sqlvars,
hs_hr_employee as Emp
LEFT JOIN li_appointments.li_appointments as PriorApp
ON Emp.Employee_ID = NextApp.Terp_ID
AND PriorApp.app_DateTime <= #DateBasis
LEFT JOIN li_appointments.li_appointments as NextApp
ON Emp.Employee_ID = NextApp.Terp_ID
AND NextApp.app_DateTime > #DateBasis
group by
Emp.Employee_ID ) EmpPrevNext
LEFT JOIN li_appointments.li_appointments as PriorApp2
ON EmpPrevNext.Employee_ID = PriorApp2.Terp_ID
AND EmpPrevNext.PrevApnt = PriorApp2.app_DateTime
LEFT JOIN li_appointments.li_appointments as NextApp2
ON EmpPrevNext.Employee_ID = NextApp2.Terp_ID
AND EmpPrevNext.NextApnt = NextApp2.app_DateTime
JOIN hs_hr_employee as Emp2
ON EmpPrevNext.Employee_ID = Emp2.Employee_ID
JOIN orangehrm_li.ohrm_employment_status as EStat
ON Emp2.Emp_Status = EStat.ID
AND EStat.`name` = 'Active'
JOIN hs_hr_emp_skill as EmpSkill
ON Emp2.emp_number = EmpSkill.emp_number
JOIN ohrm_skill as Skill
on EmpSkill.skill_id = Skill.id
AND Skill.`name` like '%Arabic%'
order by
rand();
Make sure your appointment table has an index on (Terp_ID, app_datetime )

Related

Optimize MySQL Queries with Group-Function and two different Timestamps with Join

I have got to different tables with temperature values and a timestamp. I join those tables with this query:
SELECT UNIX_TIMESTAMP(l.TimeDate) time
, AVG(l.intemp)
, AVG(n.intemp)
, DATE_FORMAT(l.TimeDate, '%Y-%m-%d-%H') dates
FROM values.temps l
LEFT
JOIN values.net n
ON DATE_FORMAT(l.TimeDate, '%Y-%m-%d-%H') = DATE_FORMAT(n.TimeDate, '%Y-%m-%d-%H')
WHERE YEARWEEK('2017-01-17 00:00:00',1) = YEARWEEK(l.TimeDate,1)
GROUP
BY dates
ORDER
BY dates ASC
This query is a little bit slow, but it works and gives me the values for 1 week. So how can I optimize it?
I haven't responded because actually I'm struggling to think how to express your YEARWEEK condition in terms of a range query.
I thought something like this would work, but it refuses to use 'range'.
SELECT *
FROM my_table
WHERE dt BETWEEN CONCAT(STR_TO_DATE(CONCAT(YEARWEEK('2017-01-25'), ' Monday'), '%x%v %W'), ' 00:00:00')
AND CONCAT(STR_TO_DATE(CONCAT(YEARWEEK('2017-01-25'), ' Sunday'), '%x%v %W'), ' 23:59:59')
Perhaps others can spot my schoolboy error.
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | my_table | ALL | dt | NULL | NULL | NULL | 100 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+

mysql left join take rows together

I have 3 tables: invoice, person and payement.
I want to have a list of invoices with the client name (from person) and the sum of payements and dates of payements(from payement).
First I made these statement
SELECT V.id, V.datum, V.amount, P.name AS 'client',
(SELECT SUM(B.amount) FROM payement AS B WHERE B.invoiceId = V.id) AS 'payed',
(SELECT GROUP_CONCAT(B.datum SEPARATOR ',') FROM payement AS B WHERE B.invoiceId = V.id) AS 'date payement'
FROM invoice AS V
JOIN person AS P ON (V.clientId = P.id)
WHERE YEAR(V.datum) = '2015'
ORDER BY V.datum;
This give what I want (p.e. a transaction of 1000 on 4 sept and one of 2400 on 10 sept), but works very slow when I have a lot of invoices.
+------+-----------+--------+--------+-------+---------------------+
| id | datum | amount | client | payed | date payement |
+------+-----------+--------+--------+-------+---------------------+
| 75 |2015-09-10 | 3400 |Sommers | 3400 |2015-09-04,2015-09-10|
+------+-----------+--------+--------+-------+---------------------+
So I tried another statement.
SELECT V.id, V.datum, V.amount, P.name AS 'client', B.amount AS 'payed', B.datum 'date payement'
FROM invoice AS V
JOIN person AS P ON (V.clientId = P.id)
LEFT JOIN payement AS B ON B.invoiceId = V.id
WHERE YEAR(V.datum) = '2015'
ORDER BY V.datum;
But this give me 2 rows for 1 invoice, when it is payed with 2 transactions.
Can I solve it with SQL, or is it better to solve it in my application (in Java)?
When an invoice has been paid with 2 payments, which details do you wish to use? the first payment or the 2nd?
Assuming that you want the total payment amount and the latest payment date:-
SELECT V.id,
V.datum,
V.amount,
P.name AS 'client',
SUM(B.amount) AS 'payed',
MAX(B.datum) AS 'date payement'
FROM invoice AS V
JOIN person AS P ON (V.clientId = P.id)
LEFT OUTER JOIN payement AS B ON B.invoiceId = V.id
WHERE YEAR(V.datum) = '2015'
GROUP BY V.id,
V.datum,
V.amount,
P.name
ORDER BY V.datum
I don't use phpmyadmin.
mysql> EXPLAIN SELECT V.factuurnr,
-> V.datum,
-> V.somexcl,
-> P.naam AS 'client',
-> SUM(B.bedrag) AS 'payed',
-> GROUP_CONCAT(DATE_FORMAT(B.datum,'%d/%m/%y') SEPARATOR ',') AS 'date payement'
-> FROM verkoop AS V
-> JOIN persoon AS P ON (V.klantId = P.id)
-> LEFT JOIN betaling AS B ON B.docId = V.id
-> WHERE YEAR(V.datum) = '2015' and month(V.datum)=9
-> GROUP BY V.factuurnr,
-> V.datum,
-> V.somexcl,
-> P.naam
-> ORDER BY factuurnr;
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+----------------------------------------------+
| 1 | SIMPLE | V | ALL | NULL | NULL | NULL | NULL | 1576 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | P | eq_ref | PRIMARY | PRIMARY | 4 | meta.V.klantId | 1 | Using where |
| 1 | SIMPLE | B | ALL | NULL | NULL | NULL | NULL | 3291 | |
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+----------------------------------------------+
3 rows in set (0.00 sec)

MySQL show used index in query

For example I have created 3 index:
click_date - transaction table, daily_metric table
order_date - transaction table
I want to check does my query use index, I use EXPLAIN function and get this result:
+----+--------------+--------------+-------+---------------+------------+---------+------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+--------------+-------+---------------+------------+---------+------+--------+----------------------------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 668 | Using temporary; Using filesort |
| 2 | DERIVED | <derived3> | ALL | NULL | NULL | NULL | NULL | 645 | |
| 2 | DERIVED | <derived4> | ALL | NULL | NULL | NULL | NULL | 495 | |
| 4 | DERIVED | transaction | ALL | order_date | NULL | NULL | NULL | 291257 | Using where; Using temporary; Using filesort |
| 3 | DERIVED | daily_metric | range | click_date | click_date | 3 | NULL | 812188 | Using where; Using temporary; Using filesort |
| 5 | UNION | <derived7> | ALL | NULL | NULL | NULL | NULL | 495 | |
| 5 | UNION | <derived6> | ALL | NULL | NULL | NULL | NULL | 645 | Using where; Not exists |
| 7 | DERIVED | transaction | ALL | order_date | NULL | NULL | NULL | 291257 | Using where; Using temporary; Using filesort |
| 6 | DERIVED | daily_metric | range | click_date | click_date | 3 | NULL | 812188 | Using where; Using temporary; Using filesort |
| NULL | UNION RESULT | <union2,5> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+--------------+-------+---------------+------------+---------+------+--------+----------------------------------------------+
In EXPLAIN results I see, that index order_date of transaction table is not used, do I correct understand ?
Index click_date of daily_metric table was used correct ?
Please tell my how to understand from EXPLAIN result does my created index is used in query properly ?
My query:
SELECT
partner_id,
the_date,
SUM(clicks) as clicks,
SUM(total_count) as total_count,
SUM(count) as count,
SUM(total_sum) as total_sum,
SUM(received_sum) as received_sum,
SUM(partner_fee) as partner_fee
FROM (
SELECT
clicks.partner_id,
clicks.click_date as the_date,
clicks,
orders.total_count,
orders.count,
orders.total_sum,
orders.received_sum,
orders.partner_fee
FROM
(SELECT
partner_id, click_date, sum(clicks) as clicks
FROM
daily_metric WHERE DATE(click_date) BETWEEN '2013-04-01' AND '2013-04-30'
GROUP BY partner_id , click_date) as clicks
LEFT JOIN
(SELECT
partner_id,
DATE(order_date) as order_dates,
SUM(order_sum) as total_sum,
SUM(customer_paid_sum) as received_sum,
SUM(partner_fee) as partner_fee,
count(*) as total_count,
count(CASE
WHEN status = 1 THEN 1
ELSE NULL
END) as count
FROM
transaction WHERE DATE(order_date) BETWEEN '2013-04-01' AND '2013-04-30'
GROUP BY DATE(order_date) , partner_id) as orders ON orders.partner_id = clicks.partner_id AND clicks.click_date = orders.order_dates
UNION ALL SELECT
orders.partner_id,
orders.order_dates as the_date,
clicks,
orders.total_count,
orders.count,
orders.total_sum,
orders.received_sum,
orders.partner_fee
FROM
(SELECT
partner_id, click_date, sum(clicks) as clicks
FROM
daily_metric WHERE DATE(click_date) BETWEEN '2013-04-01' AND '2013-04-30'
GROUP BY partner_id , click_date) as clicks
RIGHT JOIN
(SELECT
partner_id,
DATE(order_date) as order_dates,
SUM(order_sum) as total_sum,
SUM(customer_paid_sum) as received_sum,
SUM(partner_fee) as partner_fee,
count(*) as total_count,
count(CASE
WHEN status = 1 THEN 1
ELSE NULL
END) as count
FROM
transaction WHERE DATE(order_date) BETWEEN '2013-04-01' AND '2013-04-30'
GROUP BY DATE(order_date) , partner_id) as orders ON orders.partner_id = clicks.partner_id AND clicks.click_date = orders.order_dates
WHERE
clicks.partner_id is NULL
ORDER BY the_date DESC
) as t
GROUP BY the_date ORDER BY the_date DESC LIMIT 50 OFFSET 0
Although I can't explain what the EXPLAIN has dumped, I thought there must be an easier solution to what you have and came up with the following. I would suggest the following indexes to optimize your existing query for the WHERE date range and grouping by partner.
Additionally, when you have a query that uses a FUNCTION on a field, it doesn't take advantage of the index. Such as your DATE(order_date) and DATE(click_date). To allow the index to better be used, qualify the full date/time such as 12:00am (morning) up to 11:59pm. I would typically to this via
x >= someDate #12:00 and x < firstDayAfterRange.
in your example would be (notice less than May 1st which gets up to April 30th at 11:59:59pm)
click_date >= '2013-04-01' AND click_date < '2013-05-01'
Table Index
transaction (order_date, partner_id)
daily_metric (click_date, partner_id)
Now, an adjustment. Since your clicks table may have entries the transactions dont, and vice-versa, I would adjust this query to do a pre-query of all possible date/partners, then left-join to respective aggregate queries such as:
SELECT
AllParnters.Partner_ID,
AllParnters.the_Date,
coalesce( clicks.clicks, 0 ) Clicks,
coalesce( orders.total_count, 0 ) TotalCount,
coalesce( orders.count, 0 ) OrderCount,
coalesce( orders.total_sum, 0 ) OrderSum,
coalesce( orders.received_sum, 0 ) ReceivedSum,
coalesce( orders.partner_fee 0 ) PartnerFee
from
( select distinct
dm.partner_id,
DATE( dm.click_date ) as the_Date
FROM
daily_metric dm
WHERE
dm.click_date >= '2013-04-01' AND dm.click_date < '2013-05-01'
UNION
select
t.partner_id,
DATE(t.order_date) as the_Date
FROM
transaction t
WHERE
t.order_date >= '2013-04-01' AND t.order_date < '2013-05-01' ) AllParnters
LEFT JOIN
( SELECT
dm.partner_id,
DATE( dm.click_date ) sumDate,
sum( dm.clicks) as clicks
FROM
daily_metric dm
WHERE
dm.click_date >= '2013-04-01' AND dm.click_date < '2013-05-01'
GROUP BY
dm.partner_id,
DATE( dm.click_date ) ) as clicks
ON AllPartners.partner_id = clicks.partner_id
AND AllPartners.the_date = clicks.sumDate
LEFT JOIN
( SELECT
t.partner_id,
DATE(t.order_date) as sumDate,
SUM(t.order_sum) as total_sum,
SUM(t.customer_paid_sum) as received_sum,
SUM(t.partner_fee) as partner_fee,
count(*) as total_count,
count(CASE WHEN t.status = 1 THEN 1 ELSE NULL END) as COUNT
FROM
transaction t
WHERE
t.order_date >= '2013-04-01' AND t.order_date < '2013-05-01'
GROUP BY
t.partner_id,
DATE(t.order_date) ) as orders
ON AllPartners.partner_id = orders.partner_id
AND AllPartners.the_date = orders.sumDate
order by
AllPartners.the_date DESC
limit 50 offset 0
This way, the first query will be quick on the index to get all possible combinations from EITHER table. Then the left-join will AT MOST join to one row per set. If found, get the number, if not, I am applying COALESCE() so if null, defaults to zero.
CLARIFICATION.
Like you when building your pre-aggregate queries of "clicks" and "orders", the "AllPartners" is the ALIAS result of the select distinct of partners and dates within the date range you were interested in. The resulting columns of that where were "partner_id" and "the_date" respective to your next queries. So this is the basis of joining to the aggregates of "clicks" and "orders". So, since I have these two columns in the alias "AllParnters", I just grabbed those for the field list since they are LEFT-JOINed to the other aliases and may not exist in either/or the respective others.

MySQL query for distinct rows on count

I have such query that gives me results about bestseller items from shops, at the moment it works fine, but now I want to get only one product from each shop so to have a distinct si.shop_id only one bestseller product from a shop
SELECT `si`.`id`, si.shop_id,
(SELECT COUNT(*)
FROM `transaction_item` AS `tis`
JOIN `transaction` as `t`
ON `t`.`id` = `tis`.`transaction_id`
WHERE `tis`.`shop_item_id` = `si`.`id`
AND `t`.`added_date` >= '2014-02-26 00:00:00')
AS `count`
FROM `shop_item` AS `si`
INNER JOIN `transaction_item` AS `ti`
ON ti.shop_item_id = si.id
GROUP BY `si`.`id`
ORDER BY `count` DESC LIMIT 7
and that gives mu a result like:
+--------+---------+-------+
| id | shop_id | count |
+--------+---------+-------+
| 425030 | 38027 | 111 |
| 291974 | 5368 | 20 |
| 425033 | 38027 | 18 |
| 291975 | 5368 | 12 |
| 142776 | 5368 | 10 |
| 397016 | 38027 | 9 |
| 291881 | 5368 | 8 |
+--------+---------+-------+
any ideas?
EDIT
so I created a fiddle for it
http://sqlfiddle.com/#!2/cfc4c/1
Now the query returns best selling products I want it to return only one product from shopso the result of fiddle should be
+----+---------+-------+
| ID | SHOP_ID | COUNT |
+----+---------+-------+
| 1 | 222 | 3 |
| 4 | 333 | 2 |
| 8 | 555 | 1 |
| 9 | 777 | 1 |
+----+---------+-------+
Possibly something like this:-
SELECT si.shop_id,
SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(':', si.id, sub1.item_count) ORDER BY sub1.item_count DESC), ',', 1) AS `count`
FROM shop_item AS si
INNER JOIN
(
SELECT tis.shop_item_id, COUNT(*) AS item_count
FROM transaction_item AS tis
JOIN `transaction` as t
ON t.id = tis.transaction_id
AND t.added_date >= '2014-02-26 00:00:00'
GROUP BY tis.shop_item_id
) sub1
ON sub1.shop_item_id = si.id
GROUP BY si.shop_id
ORDER BY `count` DESC LIMIT 7
The sub query gets the count of items for each shop. Then the main query concatenates the item id and the item count together, group concatenates all those for a single shop together (ordered by the count descending) and then uses SUBSTRING_INDEX to grab the first one (ie, everything before the first comma).
You will have to split up the count field to get the item id and count separately (the separator is a : ).
This is taking a few guesses about what you really want, and with no table declares or data it isn't tested.
EDIT - now tested with the SQL fiddle example:-
SELECT SUBSTRING_INDEX(`count`, ':', 1) AS ID,
shop_id,
SUBSTRING_INDEX(`count`, ':', -1) AS `count`
FROM
(
SELECT si.shop_id,
SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(':', si.id, sub1.item_count) ORDER BY sub1.item_count DESC), ',', 1) AS `count`
FROM shop_item AS si
INNER JOIN transaction_item AS ti
ON ti.shop_item_id = si.id
INNER JOIN
(
SELECT tis.shop_item_id, COUNT(*) AS item_count
FROM transaction_item AS tis
JOIN `transaction` as t
ON t.id = tis.transaction_id
AND t.added_date >= '2014-02-26 00:00:00'
GROUP BY tis.shop_item_id
) sub1
ON sub1.shop_item_id = si.id
GROUP BY si.shop_id
) sub2
ORDER BY `count` DESC LIMIT 7;

Unknown column when subquery refer to outer table used at FROM clause

I have this table:
person table
| id | name |
| 1 | person1 |
| 2 | person2 |
person_grade table
| id | person_id | grade | grade_date |
| 1 | 1 | grade1 | 2010-01-01 |
| 2 | 1 | grade2 | 2012-01-01 |
| 3 | 2 | grade3 | 2010-05-05 |
| 4 | 2 | grade4 | 2012-03-03 |
I want to know person grade at a specific time, say 2012-02-02. How to achieve this?
The closest I got was, with this query:
SELECT t1.id, t1.name,
(SELECT grade FROM (
(SELECT s1.grade, s1.grade_date FROM person_grade AS s1
WHERE s1.grade_date >= '2012-02-01' AND s1.person_id = t1.id
ORDER BY s1.grade_date LIMIT 1)
UNION ALL
(SELECT s1.grade, s1.grade_date FROM person_grade AS s1
WHERE s1.grade_date <= '2012-02-01' AND s1.person_id = t1.id
ORDER BY s1.grade_date DESC LIMIT 1)
) AS ss ORDER BY grade_date LIMIT 1) AS grade_person
FROM person AS t1
But at MySQL that give me an error
"Unknown column 't1.id' in 'where clause'".
Please advise.
TIA
SELECT name,grade FROM person p
INNER JOIN person_grade pg
ON p.id=pg.person_id
WHERE pg.grade_date='2012-02-02'
I dont know about the syntax about the MySql but u can do something like this
SELECT GRADE FROM person_grade WHERE DATE(GRADE_DATE,YEAR) = DATE(SEARCH_DATE,YEAR) AND DATE(GRADE_DATE,MONTH) = DATE(SEARCH_DATE,MONTH) AND DATE(GRADE_DATE,DAY) = DATE(SEARCH_DATE,DAY)
SELECT p.name
, pg.grade
FROM person p
INNER JOIN person_grade pg ON p.id = pg.person_id
WHERE DATE(pg.grade_date) = '2012-02-02'
If this works where #mhasan's answer did not, then it's most likely because of the data type of the grade_date table. If it's DATETIME, then it stores the time element of the date and that makes searching on that field bothersome.
If you don't need the time element, try changing the data type of the column to DATE. It should use less space and make searching easier.
EDIT: Wait, I just read that question again... you don't want records where the date MATCHES the query, you want something slightly trickier. Let me think on this one...
Thanks for the replay everyone, I think I found the solution, move subquery to WHERE clause and use MIN function at date diff. Here is the query :
SELECT p.id, p.name, pg.grade, pg.grade_date
FROM person AS p
LEFT JOIN person_grade AS pg ON p.id = pg.person_id
WHERE DATEDIFF ( '2012-02-02', pg.grade_date ) =
( SELECT MIN ( DATEDIFF ( '2012-02-02', spg.grade_date ) )
FROM person AS sp
LEFT JOIN person_grade AS spg ON sp.id = spg.person_id
WHERE DATEDIFF ( '2012-02-02', spg.grade_date ) > 0 AND sp.id = p.id )
Result:
| id | name | grade | grade_date |
| 1 | person1 | grade2 | 2012-01-01 |
| 2 | person2 | grade3 | 2010-05-05 |
Seems reference to outer table doesn't work under FROM clause, but work elsewhere (at least at MySQL).
Thanks for the hint from other question: Retrieve maximal / minimal record.