SQL-запрос с использованием IN со списком очень медленно - PullRequest
0 голосов
/ 18 сентября 2018

Есть ли способ оптимизировать запрос, сделанный так:

SELECT count(*) FROM mytable 
WHERE 
indexed_field IN ('v1', 'v2', ..., 'v2000')
AND NOT regular_field='something'
AND other_regular_field='something_else';

Этот запрос "работает".Проблема в том, что это очень медленно (1мин +).Я думал, что с помощью выражения IN было бы хорошо использовать определенный список, но оказалось, что это не так хорошо.

Я использую Postgresql 9.6.

Моя таблица имеет310K.

Запрос expl:

QUERY PLAN
Aggregate  (cost=396158.70..396158.71 rows=1 width=8) (actual time=8630.558..8630.559 rows=1 loops=1)
  ->  Seq Scan on mytable  (cost=0.00..396156.77 rows=772 width=0) (actual time=7853.840..8630.478 rows=916 loops=1)
        Filter: (((non_indexed_field)::text <> 'value1'::text) AND ((non_indexed_field)::text = 'value2'::text) AND ((indexed_field)::text = ANY ('{lots and lots of values....}'::text[])))
        Rows Removed by Filter: 306768
Planning time: 2.543 ms

Execution time: 8630.770 ms

С анализом, буферы:

Aggregate  (cost=396158.70..396158.71 rows=1 width=8) (actual time=9278.560..9278.560 rows=1 loops=1)
   Buffers: shared hit=14244
   ->  Seq Scan on mytable  (cost=0.00..396156.77 rows=772 width=0) (actual time=8584.520..9278.431 rows=916 loops=1)
         Filter: (((non_indexed_field)::text <> 'value1'::text) AND ((non_indexed_field)::text = 'value2'::text) AND ((indexed_field)::text = ANY ('{lots and lots of values}'::text[])))
         Rows Removed by Filter: 306768
         Buffers: shared hit=14244
 Planning time: 1.293 ms
 Execution time: 9278.646 ms
(8 rows)

Ответы [ 3 ]

0 голосов
/ 18 сентября 2018

Я бы скорее рекомендовал использовать подвыборы (подзапросы) для предварительного выбора меньшего набора данных, например:

SELECT count(*) FROM 
(SELECT * FROM mytable 
WHERE indexed_field IN ('v1', 'v2', ..., 'v2000')) t
WHERE NOT t.regular_field='something'
AND t.other_regular_field='something_else';
0 голосов
/ 18 сентября 2018

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

indexed_field regular_field other_regular_field

как поисковая система БД, которая просматривает таблицу для всех этих трех полей и, если индекса нет, она переходит к полному сканированию таблицы. Теперь у вас есть небольшое количество данных, поэтому они возвращаются всего за 1 минуту, подумайте, если это 4-5 миллионов строк, и это займет час. Индексируйте 3 поля и посмотрите разницу. Не волнуйтесь, индекс никак не повредит.

0 голосов
/ 18 сентября 2018

Очень часто присоединение к предложению VALUES более эффективно:

SELECT count(*) 
FROM mytable 
  JOIN (
     values ('v1'), ('v2'), (...), ('v2000')
  ) as lookup(value) on lookup.value = mytable.some_column
WHERE NOT other_column = 'something'
AND another_column = 'something_else';

Обратите внимание на формат «списка». В предложении values каждое значение должно быть заключено в скобки. ('v1'), ('v2'), ... вместо ('v1', 'v2', ...)

Онлайн пример: http://rextester.com/UUWVG71217


Обратите внимание, что если столбец, с которым вы сравниваете значения, действительно является числом (например, целым числом), вы должны не указывать значения в одинарных кавычках, например, values (1),(2),(3),...(2000)

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