Each schedule has many ramps, and each ramp has an end_date
.
I need to list all my ramps, along with an extra field that contains the id of the ramp with the latest end date for that schedule. I also need to be able to use this extra field in my WHERE
clause.
This is typically a case for a sub-query using the MAX
function, except for one problem: the ramp's end_date
field can be null, indicating the ramp is current. Thus, SELECT MAX(end_date)
doesn't work, since null
values are 'smaller' than non-null values.
So far, this is what I've come up with:
SELECT r1.*,
(SELECT r2.id
FROM ramp as r2
WHERE schedule_id = r.schedule_id
ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
LIMIT 1) as latestId
FROM ramp as r1
This produces this table, which is exactly what I want:
+-------+-------------+------------+-----------------+--------+------------+------------+----------+
| id | schedule_id | suppr_flag | comment | months | start_dte | end_dte | latestId |
+-------+-------------+------------+-----------------+--------+------------+------------+----------+
| 16 | 7 | NULL | NULL | NULL | 2008-06-23 | NULL | 16 |
| 15 | 6 | NULL | NULL | NULL | 2007-05-01 | 2007-12-31 | 15 |
| 13 | 5 | NULL | 1-15 deals | NULL | 2004-08-11 | NULL | 13 |
| 11 | 4 | NULL | NULL | NULL | 2005-08-11 | NULL | 11 |
| 12 | 4 | NULL | NULL | 12 | 2004-08-11 | 2005-08-10 | 11 |
| 17 | 13 | NULL | NULL | 6 | 2009-03-05 | 2009-09-04 | 19 |
| 18 | 13 | NULL | NULL | 6 | 2009-09-05 | 2010-03-04 | 19 |
| 19 | 13 | NULL | NULL | NULL | 2010-03-05 | NULL | 19 |
| 20 | 14 | NULL | NULL | 12 | 2001-06-18 | 2008-06-17 | 20 |
except I cannot use latestId
in a WHERE clause (it's an unknown column).
Do you have any ideas?
A quick solution would be to repeat the whole query on the ORDER BY clause, because an alias is not seen there, but I don't like much how the query will look:
SELECT r1.*,
(SELECT r2.id
FROM ramp as r2
WHERE schedule_id = r1.schedule_id
ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
LIMIT 1) as latestId
FROM ramp as r1
ORDER BY
(SELECT r2.id
FROM ramp as r2
WHERE schedule_id = r1.schedule_id
ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
LIMIT 1);
Or you can SELECT from your original query, and order the result:
SELECT s.*
FROM (
SELECT r1.*,
(SELECT r2.id
FROM ramp as r2
WHERE schedule_id = r1.schedule_id
ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
LIMIT 1) as latestId
FROM ramp as r1
) s
ORDER BY s.latestId
But if I understand your logic correcty, you could use this query to get the maximum end_dte
for every schedule_id
:
SELECT schedule_id, MAX(COALESCE(end_dte, '9999-12-31')) max_dte
FROM ramp
GROUP BY schedule_id;
Then you can JOIN this query with ramp again to get the ID associated to the maximum end_dte
. On the ON clause you will neet to use COALESCE again:
SELECT r1.schedule_id, r2.id as latestId
FROM (
SELECT schedule_id, MAX(COALESCE(end_dte, '9999-12-31')) max_dte
FROM ramp
GROUP BY schedule_id) r1 INNER JOIN
ramp r2
ON r1.max_dte = COALESCE(r2.end_dte, '9999-12-31')
AND r1.schedule_id = r2.schedule_id
And then you can join this again to get the result you need:
SELECT ramp.*, m.latestId
FROM
ramp INNER JOIN (
SELECT r1.schedule_id, r2.id as latestId
FROM (
SELECT schedule_id, MAX(COALESCE(end_dte, '9999-12-31')) max_dte
FROM ramp
GROUP BY schedule_id) r1 INNER JOIN
ramp r2
ON r1.max_dte = COALESCE(r2.end_dte, '9999-12-31')
AND r1.schedule_id = r2.schedule_id
) m ON ramp.schedule_id = m.schedule_id
ORDER BY
latestId
Please see fiddle here . Notice that I am using '9999-12-31' and not '9999-99-99', the first one is a valid date, the second not.
Edit
If you also wish to consider the fact that more than one schedule_id
shares the same maximum date, and you just need the latest (maximum) ID in this case, you could use a GROUP BY query, and a MAX aggregated function:
SELECT r1.schedule_id, MAX(r2.id) as latestId
FROM (
SELECT schedule_id, MAX(COALESCE(end_dte, '9999-12-31')) max_dte
FROM ramp
GROUP BY schedule_id) r1 INNER JOIN
ramp r2
ON r1.max_dte = COALESCE(r2.end_dte, '9999-12-31')
AND r1.schedule_id = r2.schedule_id
GROUP BY
r1.schedule_id
and you use this updated version in your main query.
you could use TWO columns in your order... first for non-null indication flag, then by the date. Change what you had of...
ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
to
ORDER BY
IF( end_dte is null, 1, 2 ),
end_dte DESC
This way, it pushes all "NULL" end dates to the top of the list (via IF() value would be 1 vs 2 for anything else that HAD a date), THEN, by the date descending
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.