简体   繁体   中英

MySQL nested JSON column search and compare to with today's day

I am using MySQL Version 5.7.28. I am having json data like below.

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"}]');

As you see, the json column has nested JSON data. so here i am looking to compare today's day ( which is Saturday and we are currently in between start time and end time)

Expected Result:

(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"}]');

You need to detect day name (1) and time period (2) , and combine those two conditions by OR operator at the end.

For (1) : Detect the current day's name by using DAYNAME() function and search whether exists in the JSON data containing weekday keys through use of JSON_CONTAINS function.

For (2) : TIME() function and CAST ing strings to TIME data type might be used with a trick to add 12 hours iterations for the cases of PM type times.

So, consider using:

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)

Demo

Perhaps, something like this:

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);

First query is using JSON_EXTRACT to extract the time and JSON_UNQUOTE to remove the ( " ) then make it as a sub-query.

On the outer-query, find the weekdays value using JSON_SEARCH compare with today's DAYNAME(CURDATE()) ; if JSON_SEARCH doesn't find it, it will return NULL , hence the IS NOT NULL condition is used.

Append OR to compare CURRENT_TIME with the time value extracted from the json field. But first, using CASE expression to find out if it's AM or PM ; if it's AM , just REPLACE the AM with seconds hand ( :00 ). If it's PM , do the REPLACE like AM then convert the time value to seconds using TIME_TO_SEC then add 43200 seconds (12 hours) and convert again to standard hour:minute:second format using SEC_TO_TIME .

P/S: It was my attempt to answer but I didn't post it because I get wrong result with the time.. Only when I put CURRENT_TIME in the fiddle that I realized that the fiddle time zone is different that mine. Also, I was testing on my local DB; which is MariaDB and the results of converting string to time using STR_TO_TIME is different. Honestly, in my opinion, if this is MariaDB, I think there's a chance that the query is much shorter.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM