Данные MySQL JSON требуют SQL-запроса для сортировки массива объектов по порядку на основе значения ключа в объекте - PullRequest
0 голосов
/ 11 апреля 2019
  1. У меня есть таблица с именем posts со столбцами id, title, tags.

     CREATE TABLE IF NOT EXISTS `posts` (
       `id` int(6) unsigned NOT NULL,
       `title` varchar(100) NOT NULL,
       `tags` json NOT NULL,
       PRIMARY KEY (`id`)
     ) DEFAULT CHARSET=utf8;
    
  2. Хранит данные типа [{"tag": "android", "time": 122}, {"tag": "apple", "time": 140}] в поле tags.

      INSERT INTO `posts` (`id`, `title`, `tags`) VALUES
           ('1', 'First Post', '[{"tag": "andoroid", "time": 123}, {"tag": "mobiles", "time": 432} ]'),
           ('2', 'Second Post', '[{"tag": "apple", "time": 125}]'),
           ('3', 'Third Post', '[{"tag": "android", "time": 124}]'),
           ('4', 'Fourth Post', '[{"tag": "mobiles", "time": 472}, {"tag": "android", "time": 129}]'),
           ('5', 'Fifth Post', '[{"tag": "android", "time": 122}, {"tag": "apple", "time": 140}]'),
           ('6', 'Sixth Post', '[{"tag": "mobiles", "time": 121}, {"tag": "apple", "time": 120}]'),
           ('7', 'Seventh  Post', '[{"tag": "apple", "time": 120}, {"tag": "mobiles", "time": 130}]'),
           ('8', 'Eigth  Post', '[{"tag": "android", "time": 126}]'),
           ('9', 'Nineth  Post', '[{"tag": "mobiles", "time":132}]');
    
  3. Фильтрация данных из таблицы на основе значения тега, например. tag == "android". Для этого я использую MySQL запрос

    SELECT id, title, tags FROM posts where JSON_CONTAINS(
                     tags , '{"tag": "android"}'
                     ) ;
    

Работает нормально. Скрипт БД: https://www.db -fiddle.com / f / 5Hw1FyL3Qv2RLtCw4tZbyh / 1

Далее мне нужно упорядочить результат на основе значения time в тегах. tag == 'android' and order by time.

Заранее благодарим за помощь.

1 Ответ

2 голосов
/ 11 апреля 2019

Единственный способ решить эту проблему в версиях MySQL до 8 - использовать хранимую функцию, чтобы найти минимальное значение time из каждого набора тегов.Примерно так:

DELIMITER //
CREATE FUNCTION min_time(tags JSON)
RETURNS INT
BEGIN
  DECLARE i INT DEFAULT 0;
  DECLARE mint INT DEFAULT 9999;
  DECLARE thist INT;
  SET thist = JSON_EXTRACT(tags, CONCAT('$[', i, '].time'));
  WHILE thist IS NOT NULL DO
    IF thist < mint THEN
      SET mint = thist;
    END IF;
    SET i = i + 1;
    SET thist = JSON_EXTRACT(tags, CONCAT('$[', i, '].time'));
  END WHILE;
  RETURN mint;
END //

Тогда вы можете использовать такой запрос:

SELECT id, title, tags, min_time(tags) AS min_time
FROM posts 
WHERE JSON_CONTAINS(tags , '{"tag": "android"}')
ORDER BY min_time

Вывод:

id  title       tags                                                                tags->'$[*].time'    min_time
4   Fourth Post [{"tag": "mobiles", "time": 472}, {"tag": "android", "time": 121}]  [472, 121]          121
5   Fifth Post  [{"tag": "android", "time": 122}, {"tag": "apple", "time": 140}]    [122, 140]          122
3   Third Post  [{"tag": "android", "time": 124}]                                   [124]               124
8   Eigth Post  [{"tag": "android", "time": 126}]                                   [126]               126

Демонстрация на dbfiddle

Это может быть слишком сложно, так как сортируется по минимальному времени любого тега в сообщении.Если вы только хотите отсортировать по времени, связанному с тегом android (который вы ищете), вы можете использовать этот упрощенный запрос:

SELECT id, title, tags,
       JSON_EXTRACT(tags, CONCAT(SUBSTRING_INDEX(JSON_UNQUOTE(JSON_SEARCH(tags, 'one', 'android')), '.', 1), '.time')) AS tag_time
FROM posts 
WHERE JSON_CONTAINS(tags , '{"tag": "android"}')
ORDER BY tag_time

Вывод:

id  title       tags                                                                tag_time
3   Third Post  [{"tag": "android", "time": 75}]                                    75
4   Fourth Post [{"tag": "mobiles", "time": 472}, {"tag": "android", "time": 121}]  121
5   Fifth Post  [{"tag": "android", "time": 122}, {"tag": "apple", "time": 140}]    122
8   Eigth Post  [{"tag": "android", "time": 126}]                                   126

Демонстрация на dbfiddle

...