Помогите решить вопрос с выборкой (MySQL)

R34-zevs
На сайте с 15.07.2006
Offline
238
1013

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

Суть вопроса:

нам следует выбрать из бд (поиск идет по полю INT) все значения, начинающиеся к примеру на 18

Такой вопрос можно решить использованием LIKE, т.е. получится что-то вроде


SELECT id FROM table WHERE id LIKE '18%';

но беда в том, что LIKE использует полный перебор всех строк. А записей более 100 000, а это существенная потеря в скорости и колоссальная нагрузка. Может кто сталкивался с подобной проблемой. Подскажите, как лучше сделать выборку, возможно есть альтернативный вариант без использования LIKE?

Агентство недвижимости Фабрика Жилья (http://xn--80aacvicll3bzbzgqb.xn--p1ai/)
edogs software
На сайте с 15.12.2005
Offline
775
#1
R34-zevs:
Доброго времени суток, уважаемые. Столкнулся с проблемой выборки из MySQL.
Суть вопроса:
нам следует выбрать из бд (поиск идет по полю INT) все значения, начинающиеся к примеру на 18
Такой вопрос можно решить использованием LIKE, т.е. получится что-то вроде

SELECT id FROM table WHERE id LIKE '18%';

но беда в том, что LIKE использует полный перебор всех строк. А записей более 100 000, а это существенная потеря в скорости и колоссальная нагрузка. Может кто сталкивался с подобной проблемой. Подскажите, как лучше сделать выборку, возможно есть альтернативный вариант без использования LIKE?

Сделайте доп. поле типа tinyint допустим и повесьте на него индекс. Засуньте в него первые 2 символа int-овой строки.

like 18% будет искаться очевидно newint=18

like 1802% будет искаться newint=18 and id like '1802%' (newint=18 отсеет 99 из 100).

like 1% будет искаться как (newint=1 or newint between (10,19) ) and id like '1%' (newint отсеет опять же бОльшую часть).

Или

засуньте эти int-ты в char/varchar строку и повесьте индекс, индекс работает в чар/варчаровых полях как раз по началу строки, только убедитесь что он будет и от 1 срабатывать (по умолчанию вроде от 3 или 4 символов работает).

Разработка крупных и средних проектов. Можно с криптой. Разумные цены. Хорошее качество. Адекватный подход. Продаем lenovo legion в спб, дешевле магазинов, новые, запечатанные. Есть разные. skype: edogssoft
Милованов Ю.С
На сайте с 24.01.2008
Offline
196
#2

Если количество первых символов не определено - создайте доп.поля(char1, chars2, chars3, e.t.c) тип - целочисленный, беззнаковый(до 2^32-1)

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

WHERE `char1`='$var'

WHERE `chars2`='$var'

WHERE `chars3`='$var'

На эти поля делаем индекс.

Смотрим, как уменьшилось время запроса.

Пишим на форум, ибо интересна разница между ДО и ПОСЛЕ:)

ЗЫ. фуллТекстИндекс работает по дефолту от 4 символов. Но можно к примеру писать в БД не 18, а ХХ18, чтобы заставить работать фултекст.

Подпись))
fa2m
На сайте с 07.09.2011
Offline
23
#3

Покажите для примера несколько значений из поля INT, по которому идёт поиск? Мне кажется Вы преувеличиваете масштаб проблемы, там не аж 100 000, а всего 100 000 и запрос очень простой :) Если проект не highload, то можно и без ухищрений обойтись

Встречи в Днепре /ru/forum/736064 (/ru/forum/736064) Беклинк за копейку. Удобный сервис анализа обратных ссылок. (http://backlink.page-weight.ru/)
IL
На сайте с 20.04.2007
Offline
435
#4
R34-zevs:
возможно есть альтернативный вариант без использования LIKE?

Вы всё деревья строите? =) Вложенные множества на индексных колонках работают... Да и запрос where parent = 18 быстрее выполнится..

Да и не обязательно каждый раз всё запрашивать - можно кэширование результатов запроса использовать..

R34-zevs:
а это существенная потеря в скорости

А в числах? Может действительно

fa2m:
Вы преувеличиваете масштаб проблемы

p.s.

R34-zevs:
А записей более 100 000

это дерево категорий? =)

Милованов Ю.С:
ЗЫ. фуллТекстИндекс работает по дефолту от 4 символов.

Настраивается в конфиге мускуля, если прав хватит.. Однако, не уверен, что вариант подходящий, т.к. 18 может быть на 2-м и 3-м месте.. и, если не ошибаюсь, ТС отказался от использования разделителей (т.е. в id лежит что-то вроде 185153)

... :) Облачные серверы от RegRu - промокод 3F85-3D10-806D-7224 ( http://levik.info/regru )
fa2m
На сайте с 07.09.2011
Offline
23
#5
ivan-lev:
(т.е. в id лежит что-то вроде 185153)

Думаю именно так там и есть, ТС говорил, что тип данных INT. Поэтому обычный индекс на этот столбец Вам (ТС) в помощь и не парьте мозг :) Уверен запрос будет выполняться доли секунды.

B
На сайте с 21.12.2009
Offline
28
#6

SELECT id FROM `dump` WHERE substr( id, 1, 2 ) = 15

Таблица INNODB 490453 записи, выборка более 8000 строк, время 0.3 секунды на выборку.

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

Адский Кодер Разработка и аудит безопасности сайтов/скриптов(PHP+MySQL) (/ru/forum/530575)
Милованов Ю.С
На сайте с 24.01.2008
Offline
196
#7

Ах, да, хранимые процедуры Вам в помощь. Прирост скорости, хз какой правда, Вам гарантирован.

R34-zevs
На сайте с 15.07.2006
Offline
238
#8
Таблица INNODB 490453 записи, выборка более 8000 строк, время 0.3 секунды на выборку.

А если таблица в MyISAM? Сильная потеря в скорости будет?

---------- Добавлено 29.11.2012 в 20:21 ----------

ivan-lev:
(т.е. в id лежит что-то вроде 185153)

Да, все правильно. Именно такая конструкция планируется

---------- Добавлено 29.11.2012 в 20:27 ----------

Могу показать на примере LIKE

Бывают случаи, когда мы получаем id=182030

Это значит, что в ней отображена вся структура, т.е. id категории 1го уровня это 1, id категории 2го уровня это 1820, а третьего соответственно 182030

При условии, что средствами php мы разбили этот id. Тогда получиться так:

id1=18;

id2=1820;

id3=182030;

SELECT title FROM table WHERE id LIKE '$id{N}%';

Идея в том, что у нас есть к примеру около 100 000 записей. у каждой записи своя категория. И предположим нам нужно извлечь все записи, которые относятся к категории с id=182030;

Для неё предположим мы и должны отрисовать так называемые "хлебные крошки", но чтоб оптимизировать структуру и уменьшить кол-во запросов, мы и делаем что-то подобное, что указал выше.

Второй случай, это когда id=3294

Это значит что тут содержиться категория первого уровня id=32, категория второго уровня id = 3294

Я ранее поднимал похожий топик, изучил много материала, который мне посоветовали, много моментов уяснил по хранению, но как сделать оптимальную выборку - решил спросить у вас. Потому что есть вариант написать хранимую процедуру, но мне кажется, что можно решить и меньшими затратами.

IL
На сайте с 20.04.2007
Offline
435
#9
R34-zevs:
но как сделать оптимальную выборку - решил спросить у вас

Оптимальная с точки зрения MySQL - выбор по ключу (WHERE id IN (xx,xxxx) преобразуется к такому же виду)

WHERE id = xx OR id=xxxx OR.. итд

(проще всего проверить на реальных данных - сделать замер и оценить результат - есть ли смысл "оптимизировать")

Если есть сомнения - EXPLAIN в помощь

EXPLAIN SELECT * 

FROM `my_table`
WHERE id
IN ( 36, 39, 40 )
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE my_table range PRIMARY PRIMARY 4 NULL 3 Using where

p.p.s Если смущают лишние строчки кода - их можно оформить как метод класса или как функцию, а в коде использовать что-то вроде

$parents = get_parents($id); //$tree->getParents($id) и т.д.

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий