Как лучше оптимизировать операторы MySQL SELECT с конкретными критериями поиска? - PullRequest
0 голосов
/ 11 января 2019

Я разработал инструмент поиска изображений, чтобы проиндексировать каталог всех изображений, сохранить данные и затем получить результаты на основе поиска.

Это работает, но из-за огромного количества данных (около 2,7 миллиона строк метаданных для 150 000 изображений) возвращение результатов занимает много времени (иногда> 5 минут). Я считаю, что это связано с запросом второго набора данных в цикле foreach. Я пытаюсь объединить мой SQL в один оператор для лучшей оптимизации (если нет другого пути!).

Пока я возвращаю все image_id, в результате чего метаданные соответствуют поисковому запросу, а затем использую возвращенные image_id для получения всех метаданных в таблице метаданных для каждого изображения. Вот основная структура БД:

image_id       filename         thumb 
=============  ============     ================= 
1              image_XYZ.jpg    image_XYZ_thumb.jpg
2              emoticon.png     emoticon_thumb.jpg
3              runner_bean.jpg  runner_bean_thumb.jpg

meta_id  meta_key     meta_value      image_id
=======  ========     ==========      ========
1        filetype     jpg             1
2        keyword      runner          1
3        height       600             1
4        filetype     png             2
5        filesize     5198413         2
6        description  smiley face     2
7        filetype     jpg             3
8        filesize     12485           3
9        description  runnerbean      3
10       keyword      runner          3
11       keyword      vegetable       3

1

Пока мой код начинается с получения всех изображений, где соответствует поисковый термин "бегун" (так, изображения 1 и 3)

SELECT image_id
FROM metadata
WHERE (meta_value LIKE '%runner%')
AND meta_key IN ('keyword', 'filename', 'description')
GROUP BY image_id

2

Затем перебираем возвращенный набор результатов и получаем метаданные для каждого:

$search_results = $imagesearch->search_images(); //object array from above sql

foreach ($search_results as $image) {
    $id = $image->image_id;
    $get_metadata = $imageget->get_metadata($id)
}

3

SELECT *
FROM metadata 
WHERE image_id = $id

Затем с помощью метаданных я извлекаю строки, в которых meta_key равен данным, которые я хочу (например, высота) для получения значения и т. Д.

Итак, мой окончательный массив будет выглядеть примерно так:

Array
(
    [1] => array(
        [image_id] => 1
        [filename] => image_XYZ.jpg
        [thumb] => image_XYZ_thumb.jpg
        [filetype] => jpg
        [keyword] => runner
        [height] => 600
    ),
    [2] => array(
        [image_id] => 3
        [filename] => runner_bean.jpg
        [thumb] => runner_bean_thumb.jpg
        [filetype] => jpg
        [filesize] => 12485
        [description] => runnerbean
        [keyword] => runner
        [keyword] => vegetable
    ),
)

До сих пор я пытался использовать подзапрос, чтобы сначала получить изображения, соответствующие поисковому запросу, а затем получить метаданные для этих изображений:

SELECT *
FROM metadata m
LEFT JOIN image i ON i.image_id = m.image_id
WHERE i.image_id IN (
    SELECT image_id
    FROM metadata
    WHERE $search
    AND meta_key IN ('keyword', 'filename', 'description')
    GROUP BY image_id
)
GROUP BY m.image_id

Моя группа сортирует результаты по идентификатору изображения, поэтому я получаю только первую строку для каждого изображения (тип файла в приведенном выше примере). Есть ли способ получить окончательный массив изображений, которые соответствуют поисковому запросу, и вернуть метаданные для каждого из них более оптимизированным способом?

Любая помощь будет оценена

1 Ответ

0 голосов
/ 11 января 2019

Я не вижу, как второй GROUP BY в вашем запросе имеет смысл, я думаю, вам нужно удалить его. Затем запрос сгенерирует одну строку для каждого метаданных изображения, отсортированного по изображению. Это не совсем та структура, которую вы ожидаете (для этого потребуется развернуть набор результатов), но вы можете зациклить ее и сгенерировать массив массивов «на лету» (каждый раз, когда меняется image_id, начинайте подавать новый под-массив в вашем глобальный массив).

SELECT m.*
FROM metadata m
WHERE m.image_id IN (
    SELECT DISTINCT image_id
    FROM metadata
    WHERE 
        meta_value LIKE '%runner%'
        AND meta_key IN ('keyword', 'filename', 'description')
)
ORDER BY m.image_id

Примечание: в зависимости от того, как выглядят ваши данные, SELECT DISTINCT во внутреннем запросе может быть более эффективным, чем SELECT.

Другой вариант (который производит тот же набор результатов) - использовать коррелированный подзапрос с предложением WHERE EXISTS, как показано ниже:

SELECT m.*
FROM metadata m
WHERE EXISTS (
    SELECT 1
    FROM metadata
    WHERE 
        image_id = m.image_id
        AND meta_value LIKE '%runner%'
        AND meta_key IN ('keyword', 'filename', 'description')    
)
ORDER BY m.image_id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...