MySQL 嵌套 JSON 列搜索并与今天进行比较
MySQL nested JSON column search and compare to with today's day
我正在使用 MySQL 版本 5.7.28。我有如下 json 数据。
CREATE TABLE `week2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` smallint(1),
`json` text ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO week2(id,type,json)
VALUES
(121,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"},{"weekdays":"Tuesday"},{"weekdays":"Wednesday"},{"weekdays":"Thursday"},{"weekdays":"Friday"},{"weekdays":"Saturday"}]'),
(122,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"}]'),
(123,2,'[{"start_time":"08:00 AM","end_time":"10:00 PM"}]');
如您所见,json 列嵌套了 JSON 数据。所以在这里我想比较今天(星期六,我们目前处于开始时间和结束时间之间)
预期结果:
(121,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"},{"weekdays":"Tuesday"},{"weekdays":"Wednesday"},{"weekdays":"Thursday"},{"weekdays":"Friday"},{"weekdays":"Saturday"}]'),
(123,2,'[{"start_time":"08:00 AM","end_time":"10:00 PM"}]');
您需要检测日期名称 (1) 和时间段 (2),并通过 [=28] 组合这两个条件=]OR 运算符在末尾。
For (1) : 使用DAYNAME()
函数检测当天的名称,通过use查找JSON数据中是否存在包含weekday keys JSON_CONTAINS
函数。
对于 (2) : TIME()
函数和 CAST
ing 字符串到 TIME
数据类型可能会使用一个技巧来添加PM
类型时间的案例迭代 12 小时。
所以,考虑使用:
SELECT *
FROM `week2`
WHERE JSON_CONTAINS(`json`->>'$[*].weekdays', CONCAT('"',DAYNAME( NOW() ),'"')) = 1
OR
(
TIME(ADDTIME(NOW(),"8:00:00")) >=
CASE WHEN INSTR(REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM"),"PM")>0
THEN
CAST(CONCAT(MOD((TIME_FORMAT(REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM"), "%T")+12),24),":00 AM")
AS TIME)
ELSE
CAST((REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM")) AS TIME)
END
AND
TIME(ADDTIME(NOW(),"8:00:00")) <=
CASE WHEN INSTR(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM"),"PM")>0
THEN
CAST(CONCAT(MOD((TIME_FORMAT(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM"), "%T")+12),24),":00 AM")
AS TIME)
ELSE
CAST(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM") AS TIME)
END)
也许,像这样:
SELECT *,CURRENT_TIME
FROM
(SELECT *,JSON_UNQUOTE(JSON_EXTRACT(`json`,'$[0].start_time')) AS st,
JSON_UNQUOTE(JSON_EXTRACT(`json`,'$[0].end_time')) AS et FROM week2) V
WHERE (JSON_SEARCH(`json`, 'one', DAYNAME(CURDATE())) IS NOT NULL
OR
CURRENT_TIME hour >=
CASE WHEN st LIKE '%AM%' THEN REPLACE(st,' AM',':00')
WHEN st LIKE '%PM%' THEN SEC_TO_TIME(TIME_TO_SEC(REPLACE(st,' PM',':00'))+43200)
END
AND
CURRENT_TIME hour <=
CASE WHEN et LIKE '%AM%' THEN REPLACE(et,' AM',':00')
WHEN et LIKE '%PM%' THEN SEC_TO_TIME(TIME_TO_SEC(REPLACE(et,' PM',':00'))+43200)
END);
第一个查询使用 JSON_EXTRACT to extract the time and JSON_UNQUOTE 删除 ("
) 然后将其作为子查询。
在外部查询中,使用 JSON_SEARCH 与今天的 DAYNAME(CURDATE())
进行比较,找到 weekdays
值;如果 JSON_SEARCH
没有找到,它将 return NULL
,因此使用 IS NOT NULL
条件。
追加 OR
以将 CURRENT_TIME
与从 json
字段中提取的时间值进行比较。但首先,使用 CASE
表达式找出它是 AM
还是 PM
;如果是 AM
,只是 REPLACE
秒针 AM
(:00
)。如果是 PM
,请像 AM
一样执行 REPLACE
然后使用 TIME_TO_SEC
将时间值转换为秒,然后添加 43200 秒(12 小时)并再次转换为标准 hour:minute:second
格式使用 SEC_TO_TIME
.
P/S:这是我试图回答的,但我没有 post 因为我得到了错误的结果。只有当我把 CURRENT_TIME
放在 fiddle 我意识到 fiddle 时区与我的不同。另外,我正在本地数据库上进行测试;这是 MariaDB,使用 STR_TO_TIME
将字符串转换为时间的结果是不同的。老实说,在我看来,如果这是 MariaDB,我认为查询可能会更短。
我正在使用 MySQL 版本 5.7.28。我有如下 json 数据。
CREATE TABLE `week2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` smallint(1),
`json` text ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO week2(id,type,json)
VALUES
(121,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"},{"weekdays":"Tuesday"},{"weekdays":"Wednesday"},{"weekdays":"Thursday"},{"weekdays":"Friday"},{"weekdays":"Saturday"}]'),
(122,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"}]'),
(123,2,'[{"start_time":"08:00 AM","end_time":"10:00 PM"}]');
如您所见,json 列嵌套了 JSON 数据。所以在这里我想比较今天(星期六,我们目前处于开始时间和结束时间之间)
预期结果:
(121,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"},{"weekdays":"Tuesday"},{"weekdays":"Wednesday"},{"weekdays":"Thursday"},{"weekdays":"Friday"},{"weekdays":"Saturday"}]'),
(123,2,'[{"start_time":"08:00 AM","end_time":"10:00 PM"}]');
您需要检测日期名称 (1) 和时间段 (2),并通过 [=28] 组合这两个条件=]OR 运算符在末尾。
For (1) : 使用DAYNAME()
函数检测当天的名称,通过use查找JSON数据中是否存在包含weekday keys JSON_CONTAINS
函数。
对于 (2) : TIME()
函数和 CAST
ing 字符串到 TIME
数据类型可能会使用一个技巧来添加PM
类型时间的案例迭代 12 小时。
所以,考虑使用:
SELECT *
FROM `week2`
WHERE JSON_CONTAINS(`json`->>'$[*].weekdays', CONCAT('"',DAYNAME( NOW() ),'"')) = 1
OR
(
TIME(ADDTIME(NOW(),"8:00:00")) >=
CASE WHEN INSTR(REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM"),"PM")>0
THEN
CAST(CONCAT(MOD((TIME_FORMAT(REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM"), "%T")+12),24),":00 AM")
AS TIME)
ELSE
CAST((REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM")) AS TIME)
END
AND
TIME(ADDTIME(NOW(),"8:00:00")) <=
CASE WHEN INSTR(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM"),"PM")>0
THEN
CAST(CONCAT(MOD((TIME_FORMAT(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM"), "%T")+12),24),":00 AM")
AS TIME)
ELSE
CAST(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM") AS TIME)
END)
也许,像这样:
SELECT *,CURRENT_TIME
FROM
(SELECT *,JSON_UNQUOTE(JSON_EXTRACT(`json`,'$[0].start_time')) AS st,
JSON_UNQUOTE(JSON_EXTRACT(`json`,'$[0].end_time')) AS et FROM week2) V
WHERE (JSON_SEARCH(`json`, 'one', DAYNAME(CURDATE())) IS NOT NULL
OR
CURRENT_TIME hour >=
CASE WHEN st LIKE '%AM%' THEN REPLACE(st,' AM',':00')
WHEN st LIKE '%PM%' THEN SEC_TO_TIME(TIME_TO_SEC(REPLACE(st,' PM',':00'))+43200)
END
AND
CURRENT_TIME hour <=
CASE WHEN et LIKE '%AM%' THEN REPLACE(et,' AM',':00')
WHEN et LIKE '%PM%' THEN SEC_TO_TIME(TIME_TO_SEC(REPLACE(et,' PM',':00'))+43200)
END);
第一个查询使用 JSON_EXTRACT to extract the time and JSON_UNQUOTE 删除 ("
) 然后将其作为子查询。
在外部查询中,使用 JSON_SEARCH 与今天的 DAYNAME(CURDATE())
进行比较,找到 weekdays
值;如果 JSON_SEARCH
没有找到,它将 return NULL
,因此使用 IS NOT NULL
条件。
追加 OR
以将 CURRENT_TIME
与从 json
字段中提取的时间值进行比较。但首先,使用 CASE
表达式找出它是 AM
还是 PM
;如果是 AM
,只是 REPLACE
秒针 AM
(:00
)。如果是 PM
,请像 AM
一样执行 REPLACE
然后使用 TIME_TO_SEC
将时间值转换为秒,然后添加 43200 秒(12 小时)并再次转换为标准 hour:minute:second
格式使用 SEC_TO_TIME
.
P/S:这是我试图回答的,但我没有 post 因为我得到了错误的结果。只有当我把 CURRENT_TIME
放在 fiddle 我意识到 fiddle 时区与我的不同。另外,我正在本地数据库上进行测试;这是 MariaDB,使用 STR_TO_TIME
将字符串转换为时间的结果是不同的。老实说,在我看来,如果这是 MariaDB,我认为查询可能会更短。