оптимизировать вторичный индекс и запрос диапазона дат - PullRequest
1 голос
/ 12 февраля 2020

Я выполняю агрегированный запрос, который занимает намного больше времени, чем ожидалось. Запрос из одной таблицы без объединений. Предложение where включает диапазон дат, предложение in и столбец даты. В таблице всего около 5 тыс. Строк, а время запроса составляет 13 с.

Запрос:

select `site_id`, created_year_month_idx as time_column, count(*) as total 
from `patients` 
where `created_year_month_idx` between 20080101 and 20090101 and 
   `site_id` in (1,2,3) and 
   `patients`.`deleted_at` is null 
group by `created_year_month_idx`, `site_id`

Когда я объясняю запрос, кажется, что он выполняет сканирование всей таблицы:

| id  | select_type | table    | partitions | type  | possible_keys                                 | key                                   | key_len | ref | rows | filtered | Extra                                        |
| --- | ----------- | -------- | ---------- | ----- | --------------------------------------------- | ------------------------------------- | ------- | --- | ---- | -------- | -------------------------------------------- |
| 1   | SIMPLE      | patients |            | range | site_id,patients_created_year_month_idx_index | patients_created_year_month_idx_index | 4       |     | 1    | 100      | Using where; Using temporary; Using filesort |

Операторы создания таблицы:

CREATE TABLE `sites` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(10),
 PRIMARY KEY (`id`)
);

CREATE TABLE `patients` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `site_id` int(10) unsigned NOT NULL,
 `created_at` timestamp NULL DEFAULT NULL,
 `deleted_at` timestamp NULL DEFAULT NULL,
 `created_year_month_idx` date GENERATED ALWAYS AS (date_format(`created_at`,'%Y-%m-01')) VIRTUAL,
 PRIMARY KEY (`id`),
 KEY `site_id` (`site_id`),
 KEY `patients_created_year_month_idx_index` (`created_year_month_idx`),
 CONSTRAINT `patients_site` FOREIGN KEY (`site_id`) REFERENCES `sites` (`id`)
);

Я создал скрипту БД в https://www.db-fiddle.com/f/4zbjFpMYXEGSviprQcaTm3/0

(кстати, если вы можете сказать мне, как отформатировать таблицу уценки в SO, я исправить вышеперечисленное)

Ответы [ 3 ]

1 голос
/ 12 февраля 2020

Я голосую за

INDEX(`deleted_at`, `created_year_month_idx`, `site_id`)

Но в основном потому, что это "покрытие". deleted_at - это первое, поскольку это, по сути, тест на равенство (IS NULL).

Понимаете ли вы, что у вас есть один год плюс один день? BETWEEN 20080101 AND 20090101

Вы действительно хотите около 1K строк вывода?

0 голосов
/ 12 февраля 2020

Попробуйте эту версию запроса со связанными индексами:

select site_id, created_year_month_idx as time_column, count(*) as total 
from patients p
where created_year_month_idx` between 20080101 and 20090101 and 
      site_id = 1 and 
      p.deleted_at is null 
group by site_id, created_year_month_idx
union all
select site_id, created_year_month_idx as time_column, count(*) as total 
from patients p
where created_year_month_idx` between 20080101 and 20090101 and 
      site_id = 2 and 
      p.deleted_at is null 
group by site_id, created_year_month_idx
union all
select site_id, created_year_month_idx as time_column, count(*) as total 
from patients p
where created_year_month_idx` between 20080101 and 20090101 and 
      site_id = 3 and 
      p.deleted_at is null 
group by site_id, created_year_month_idx;

Тогда индекс включен patients(site_id, created_year_month_idx, deleted_id).

0 голосов
/ 12 февраля 2020

Я не могу сказать, если это быстрее, так как в таблицах нет записей, и все заканчивается за 1 мс или меньше, но попробуйте присоединиться к таблице сайтов и затем использовать первичный ключ для IN вместо внешнего ключа, как это:

 SELECT s.id, p.created_year_month_idx AS time_column, COUNT(*) AS total 
 FROM patients p
 JOIN sites s ON s.id = p.site_id
 WHERE p.created_year_month_idx BETWEEN 20080101 AND 20090101 
 AND s.id IN (1,2,3) 
 AND p.deleted_at IS NULL 
 GROUP BY p.created_year_month_idx, s.id

РЕДАКТИРОВАТЬ: причина, почему запрос медленный, потому что планировщик запросов не использует ни один из ваших индексов. Выше будет использоваться индекс первичного ключа.

...