У меня есть база данных с 500 тыс. Профилей компаний + места, где они предоставляют свои услуги. Таким образом, у меня есть таблица таблиц таблиц компаний. Компания может обслуживать всю страну или только в городе. Таблица мест выглядит следующим образом:
ID | company_id | scope | country_id | city_id
1 | 'companyuuid...' | 'city' | 'UK' | '32321'
2 | 'companyuuid...' | 'country' | 'US' | NULL
Когда компания предоставляет услуги по всей стране, мы указываем сферу действия "страна", и у нас есть область "город", когда компания предоставляет услуги только в определенном городе.
К сожалению, MySQL довольно медленно обрабатывает запросы, когда у них есть оператор "OR" и с учетом суммы, с которой нам нужно работать, запросы должны быть максимально оптимизированы.
select distinct companies.id from companies
inner join locations on companies.id = locations.company_id
and (locations.scope = 'city' and locations.city_id = '703448' )
order by companies.score desc limit 12 offset 0
Моя текущая проблема заключается в том, что при поиске компаний в городе мне также нужно показывать компании, предоставляющие услуги по всей стране. Очевидным способом было бы добавить инструкцию OR следующим образом:
select distinct companies.id from companies
inner join locations on companies.id = locations.company_id
and (locations.scope = 'city' and locations.city_id = '703448' )
or (locations.scope = 'country' and locations.country_id = 'UK' )
order by companies.score desc limit 12 offset 0
НО проблема в том, что оператор OR сделает запрос чрезвычайно медленным. Есть ли другой способ использовать дополнительное соединение, возможно, поэтому мы можем быстро сохранить запрос?
Проблема 1: OR
кажется "неправильной". Вы хотите, чтобы все города в Великобритании, а также все Лондоны, в том числе и в Канаде.
Вы, вероятно, хотите AND
вместо OR
. И вам понадобится "самостоятельное соединение", чтобы дважды попасть в locations
? Схема EAV сосет.
Задача 2: x AND y OR z
(x AND y) OR z
, а не x AND (y OR z)
.
Я бы рекомендовал использовать exists
:
select c.id
from companies c
where exists (select 1
from locations l
where l.company_id = c.id and
l.scope = 'city' and
l.city_id = 703448 -- I'm guessing city_id is a number, so no quotes
) or
exists (select 1
from locations l
where l.company_id = c.id and l.scope = 'country'
)
order by c.score desc
limit 12 offset 0;
exists
подзапросы могут использовать индекс для locations(company_id, scope, city_id)
. Запрос может даже иметь возможность использовать индекс для companies(score)
.
join
с or
занимает всего 6 мс? Это поразительно. У вас есть рекомендуемые join
для этого запроса?
union
вместоor