Как оптимизировать SQL-запрос, который сочетает в себе INNER JOINs, DISTINCT и WHERE? - PullRequest
0 голосов
/ 04 мая 2018
SELECT DISTINCT options.id, options.foo_option_id, options.description
FROM vehicles 
INNER JOIN vehicle_options     ON vehicle_options.vehicle_id = vehicles.id 
INNER JOIN options             ON options.id = vehicle_options.option_id 
INNER JOIN discounted_vehicles ON vehicles.id = discounted_vehicles.vehicle_id 
WHERE discounted_vehicles.discount_id = 4;

Приведенный выше запрос возвращает мне 2067 строк, и он выполняется локально за 1,7 секунды. Мне интересно, насколько это быстро, насколько это возможно, или я могу как-то настроить его, так как этот набор данных будет быстро расти со временем.

Вещи, которые я пробовал без изменения скорости:

1 - изменить порядок объединения, объединяя таблицы от самых маленьких до самых больших.

2 - добавление индекса в discided_vehicles.discount_id.

Ответы [ 4 ]

0 голосов
/ 04 мая 2018

Лучший запрос зависит от недостающей информации.
Это должно быть значительно быстрее в типичной установке:

SELECT id, foo_option_id, description
FROM   options o
WHERE  EXISTS (
   SELECT
   FROM   discounted_vehicles d
   JOIN   vehicle_options vo USING (vehicle_id)
   WHERE  d.discount_id = 4
   AND    vo.option_id = o.id
   );

Предполагая ссылочную целостность, обеспечиваемую ограничениями FK, мы можем опустить таблицу vehicle в запросе и напрямую соединиться с discounted_vehicles до vehicle_options.

Кроме того, EXISTS, как правило, быстрее, если имеется много подходящих строк для каждого отдельного параметра.

В идеале вы должны иметь многоколоночные индексы:

discounted_vehicles(discount_id, vehicle_id)
vehicle_options(vehicle_id, option_id)

Индекс столбцы в этом порядке. Возможно, у вас есть ограничение PK на vehicle_options, обеспечивающее второй индекс, но порядок столбцов должен совпадать. Связанный:

В зависимости от фактического распределения данных могут быть более быстрые стили запросов. Связанный:

Изменение порядка соединения обычно бесполезно . Postgres reorders присоединяется любым способом, который ожидается самым быстрым. (Применяются исключения.) Похожие:

0 голосов
/ 04 мая 2018

Попробуйте использовать групповой вместо отдельных

SELECT 
    "options"."id",
    "options"."foo_option_id",
    "options"."description"
FROM
    "vehicles" 
    INNER JOIN "vehicle_options" ON "vehicle_options"."vehicle_id" = "vehicles"."id" 
    INNER JOIN "options" ON "options"."id" = "vehicle_options"."option_id" 
    INNER JOIN "discounted_vehicles" ON "vehicles"."id" = "discounted_vehicles"."vehicle_id" 
WHERE 
    "discounted_vehicles"."discount_id" = 4 
GROUP BY 
    "options.id";
0 голосов
/ 04 мая 2018

Хотя вам необходимо создать необходимые индексы, прежде чем пытаться выполнить запрос ниже

SELECT "options"."id", "options"."foo_option_id",
    "options"."description"
  FROM "vehicles" 
  INNER JOIN "vehicle_options" 
    ON "vehicle_options"."vehicle_id" = "vehicles"."id" 
  INNER JOIN "options" 
    ON "options"."id" = "vehicle_options"."option_id" 
  INNER JOIN "discounted_vehicles" 
    ON "vehicles"."id" = "discounted_vehicles"."vehicle_id" 
  WHERE "discounted_vehicles"."discount_id" = 4
  GROUP BY options"."id", "options"."foo_option_id",
    "options"."description"
0 голосов
/ 04 мая 2018

1 - Изменить порядок объединения, объединяя таблицы от самых маленьких до самых больших.

За кулисами PostgreSQL меняет порядок таблиц в соответствии с планом объяснения, который разрабатывает оптимизатор SQL. Заказ, который вы написали, не имеет смысла.

2 - добавление индекса в discided_vehicles.discount_id.

Зависит от того, насколько избирателен столбец discount_id. Как вы думаете, это отфильтровывает 95% строк, оставляя только 5%? Если оно оставляет 5% или менее, индекс поможет. В противном случае полное сканирование таблицы выполняется быстрее.

Кроме того, если его еще нет, я бы добавил индекс:

vehicle_options (vehicle_id)

Но, возможно, он уже создан внешним ключом.

...