(Firebird) SQL: ограничение 100x в предложении WHERE делает запрос чрезвычайно медленным - PullRequest
0 голосов
/ 16 сентября 2009

Я использую Firebird и создал таблицу, которая называется EVENTS. Столбцы:

id (INT) | name (VARCHAR) | category (INT) | website (VARCHAR) | lat (DOUBLE) | lon (DOUBLE)

Теперь пользователь хочет искать события в определенном радиусе вокруг него, но вводит только две или три буквы своего родного города. Итак, у нас есть, скажем, 200 возможных городов с их широтой и долготой. Так что мой SQL-запрос выглядит так:

SELECT id FROM events WHERE ((lat BETWEEN 30.09 AND 30.12) AND (lon BETWEEN 40.78 AND 40.81)) OR ((lat BETWEEN 30.09 AND 30.12) AND (lon BETWEEN 40.78 AND 40.81)) OR ...

Таким образом, мы получаем 200 ограничений в предложении WHERE, и для получения результата требуется несколько секунд.

Я знаю, что запрос может выглядеть ужасно. Но являются ли многие ограничения действительно узким местом? Можно ли оптимизировать этот запрос?

Ответы [ 6 ]

2 голосов
/ 16 сентября 2009

Я предполагаю, что ядро ​​СУБД решит, что критерий, скорее всего, вернет много строк, поэтому неправильно сканирует таблицу. Подсказка, чтобы сделать правильную вещь, или выполнить какое-то переписывание запроса, например, (что может или не может помочь)

SELECT id
  FROM cities c
  JOIN events e ON (e.lat BETWEEN c.lat - .01 AND c.lat + .01) AND (e.lon BETWEEN c.lon - .01 AND c.lon + .01)
 WHERE c.name LIKE 'x%'

На сервере SQL вы можете написать

SELECT id
  FROM cities c
  INNER LOOP JOIN events e ON (e.lat BETWEEN c.lat - .01 AND c.lat + .01) AND (e.lon BETWEEN c.lon - .01 AND c.lon + .01)
 WHERE c.name LIKE 'x%'

для обеспечения правильного плана (у вас есть индекс для столбцов lat и lon вместе?)

1 голос
/ 16 сентября 2009

Компромисс для скорости:

Города не двигаются. Всякий раз, когда вы добавляете событие, вы можете предварительно рассчитать расстояние между каждым событием и каждым городом и сохранить расстояние до всех близлежащих городов. Вы можете индексировать это по городам, чтобы вы могли непосредственно находить события рядом с данным городом (или около 200 городов с одинаковым префиксом). Фактическая фильтрация долготы / широты может быть ограничена гораздо меньшим набором событий.

0 голосов
/ 04 февраля 2010

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

select *
from events e
where exists
( select *
  from cities c
  where c.name like 'X%' and
        e.lat BETWEEN c.lat - .01 AND c.lat + .01 and
        e.lon BETWEEN c.lon - .01 AND c.lon + .01
)

В некоторых сценариях он работает быстрее, чем соединения.

0 голосов
/ 16 сентября 2009

Сначала вы должны использовать IBExpert над своим запросом, чтобы проверить его план и понять, почему он такой медленный.

0 голосов
/ 16 сентября 2009

Создайте удобный для поиска диапазон индекс (индекс B-дерева) на events.lat и / или events.long (но не один индекс на обоих!), Который, по крайней мере, поможет вам в этом.

Что вам действительно нужно, так это R-дерево или подобное, которое позволяет индексировать многомерные данные и обеспечивает хорошую производительность поиска по диапазону. В PostgreSQL есть GiST для этого; Я не знаю, какую поддержку имеет Firebird для решения подобных проблем.

Wiki ссылки для получения дополнительной информации: http://en.wikipedia.org/wiki/R-tree http://en.wikipedia.org/wiki/GiST

0 голосов
/ 16 сентября 2009

Вы можете перепроектировать базу данных (если это возможно), чтобы она содержала не только широту и долготу, но и название места события. Ваш запрос будет содержать оператор like или аналогичный (begins with?). Я знаю, что это может быть непригодным решением, но ограничение себя квадратными (в сферическом смысле) городами или регионами кажется мне немного странным;)

...