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

У меня есть некоторый опыт работы с SQL, но я все еще не могу выяснить, как я могу сделать следующие показатели эффективности запросов эффективными.

У меня есть 2 таблицы - Box и Item.Box имеет атрибут id, который является первичным ключом (и некоторые другие), а Item имеет box_id, type, name.Каждая таблица содержит миллиарды записей, в каждой коробке в среднем 10 элементов. Я хочу запросить все поля, в которых есть хотя бы один элемент с данным типом и хотя бы один элемент с заданным именем (может быть один и тот же элемент или другой). Запрос должен быть разбит на страницыразмер 10. Я использовал индексирование по одному столбцу для всех атрибутов Item.Следующий запрос для этого (первая страница) занимает очень много времени (более минуты):

SELECT Box.id FROM Box WHERE (EXISTS (SELECT 1 FROM Item WHERE Item.box_id = Box.id AND Item.type = 'my_type')) AND (EXISTS (SELECT 1 FROM Item WHERE Item.box_id = Box.id AND Item.name = 'my_name')) LIMIT 10

Я думаю, что проблема заключается в том, чтобы пересечение между ячейками было отфильтрованокаждая часть запроса (запрос только с одним из ограничений возвращает около миллиона записей).Я использую Aurora PostgreSQL 9.6.6.

Ответы [ 3 ]

0 голосов
/ 10 октября 2018

Вы не ответили на разъяснения, поэтому я приму несколько вещей:

  • Вы хотите ВСЕ коробки, а не только 10 из них.
  • Существует опечатка, когдасравнивая по имени.Должно быть: Item.name = 'my_name'
  • Вы сказали: «Я проиндексировал все атрибуты предмета».Я бы предположил, что у вас есть индексы из одного столбца для всех столбцов таблицы Item.
  • Столбец id в Box является первичным ключом, и поэтому у него уже есть индекс.

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

create index ix1 on Item (box_id, type);

create index ix2 on Item (box_id, name);

Да, оба из них.Повторите запрос еще раз и посмотрите, сколько времени это займет.

Если все еще медленно, пожалуйста, опубликуйте план объяснения, используя:

EXPLAIN ANALYZE
SELECT Box.id 
  FROM Box 
  WHERE 
(EXISTS (SELECT 1 FROM Item WHERE Item.box_id = Box.id AND Item.type = 'my_type')) 
  AND
(EXISTS (SELECT 1 FROM Item WHERE Item.box_id = Box.id AND Item.name = 'my_name'))
0 голосов
/ 10 октября 2018

INTERSECT - это еще один вариант.

  SELECT Box_id FROM Item
  WHERE Item.type = 'my_type'
  INTERSECT
  SELECT Box_id FROM Item 
  WHERE Item.name = 'my_name'

Примечание: INTERSECT возвращает различные значения, поэтому нет необходимости во внешнем запросе для получения списка различных значений Box_id, которые соответствуют вашим критериям.Этот запрос возвращает бесхозные элементы (элементы с box_id, отсутствующим в таблице блоков), поэтому в этом случае может потребоваться внешний запрос.

0 голосов
/ 10 октября 2018

Примерно так?

SELECT DISTINCT ON (Box.id) Box.*
FROM Box
  JOIN Item I1 ON I1.box_id = Box.id AND I1.type = 'my_type'
  JOIN Item I2 ON I2.box_id = Box.id AND I2.name = 'my_name'
ORDER BY Box.id;

JOIN s фильтрует результаты по типу и имени элемента.

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