如何在 MySQL 中使用 JSON 文档加入子查询?

How to Join the subquery using JSON document within MySQL?

Mysql 版本为 8.0.18-commercial。 table 的主键是 id。 我编写了以下查询,其中显示 hostnamedetails

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"
  }
]

我只想从具有最新 activityDate

的元素中获取 Msg

我想要的输出显示 hostname 以及带有 latest date 的元素的 Msg :

hostname        Msg
abc123          Job failed

我已经编写了以下查询,它 运行 成功但根本没有显示任何内容。此外,它需要 17secs 来执行。

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;

也许我是老派,但日期字段应该作为单独的字段存储在 JSON 之外,以便于查询。

ID是自增的,数据是按时间戳顺序插入的吗?如果是,那么您可以 运行 这样的查询为您提供每个主机名的最后一行:

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) ;
  • 您需要通过删除末尾的逗号来修复 JSON 的格式 以 "activityDate" keys
  • 开头的行数
  • 转换函数如STR_TO_DATE()应该应用到 派生的 activityDate 列以获得订购日期(不是 characterwise) 结果。
  • 通过ROW_NUMBER()解析不需要子查询 ORDER BY 子句 ( with descending order 旁边的函数,并添加一个 LIMIT 1 子句 在查询结束时

因此,您可以将查询重写为

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   

Demo

更新 :

对于有多个id值的情况,可以考虑在子查询中使用ROW_NUMBER()函数,在主查询中过滤掉返回等于1的值:

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

Demo

另一种方法使用 ROW_NUMBER() 函数和 LIMIT 子句包含相关子查询,并且适用于具有多个 id 值的记录:

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

Demo