The Mysql version is 8.0.18-commercial. The primary key of the table is id.
I have written the following query which displays hostname and details columns
select hostname, details from table t1;
hostname: abc123
details:
[
{
"Msg": "Job Running",
"currentTask": "IN_PROGRESS",
"activityDate": "2020-07-20 16:25:15"
},
{
"Msg": "Job failed",
"currentTask": "IN_PROGRESS",
"activityDate": "2020-07-20 16:35:24"
}
]
I want the Msg value only from the element having most recent activityDate
My desired output is displaying hostname alongwith Msg of the element with latest date :
hostname Msg
abc123 Job failed
I have written following query and it is running successfully but not displaying anything at all. Morever, it is taking 17secs to execute.
select hostname,
(select Msg
from (
select x.*, row_number() over(partition by t.id order by x.activityDate) rn
from table1 t
cross join json_table(
t.audits,
'$[*]' columns(
Msg varchar(50) path '$.Msg',
activityDate datetime path '$.activityDate'
)
) x
) t
where rn = 1) AS Msg
from table1;
"activityDate" keysSTR_TO_DATE() should be applied to
the derived activityDate columns in order to get date ordered(not
characterwise) results.ROW_NUMBER() analytic
function next to the ORDER BY Clause( with descending order ), and adding a LIMIT 1 Clause
at the end of the querySo, you can rewrite the query as
SELECT t1.hostname,
j.Msg
FROM t1
CROSS JOIN
JSON_TABLE(details, '$[*]'
COLUMNS (
Msg VARCHAR(100) PATH '$.Msg',
activityDate VARCHAR(100) PATH '$.activityDate'
)
) j
ORDER BY ROW_NUMBER()
OVER ( -- PARTITION BY id
ORDER BY STR_TO_DATE(j.activityDate, '%Y-%m-%d %H:%i:%S') DESC)
LIMIT 1
Update :
For the case of having several id values, you may consider using the ROW_NUMBER() function within a subquery and filter out the values returning equal to 1 in the main query :
SELECT id, Msg
FROM
(
SELECT t1.*, j.Msg,
ROW_NUMBER()
OVER (PARTITION BY id
ORDER BY STR_TO_DATE(j.activityDate, '%Y-%m-%d %H:%i:%S') DESC) AS rn
FROM t1
CROSS JOIN
JSON_TABLE(details, '$[*]'
COLUMNS (
Msg VARCHAR(100) PATH '$.Msg',
activityDate VARCHAR(100) PATH '$.activityDate'
)
) j
) q
WHERE rn= 1
One another method uses ROW_NUMBER() function together with LIMIT clause contains Correlated Subquery, and works for records with multiple id values :
SELECT t.id,
( SELECT j.Msg
FROM t1
CROSS JOIN
JSON_TABLE(details, '$[*]'
COLUMNS (
Msg VARCHAR(100) PATH '$.Msg',
activityDate VARCHAR(100) PATH '$.activityDate'
)
) j
WHERE t1.id = t.id
ORDER BY ROW_NUMBER()
OVER (ORDER BY STR_TO_DATE(j.activityDate, '%Y-%m-%d %H:%i:%S') DESC)
LIMIT 1 ) AS Msg
FROM t1 AS t
Maybe I'm old school, but the date field should be stored as a separate field either in additional to the JSON, to allow for easy queries.
Is the ID auto increment, and is the data inserted in timestamp order? If yes, then you can run a query like this to give you the last row for each hostname:
SELECT id, hostname, details
FROM table t1
WHERE NOT EXISTS (SELECT 1 FROM table t2 WHERE t2.hostname = t1.hostname AND t2.id > t1.id) ;