Более быстрый способ сопоставления строки в MySQL с использованием замены

0

У меня есть интересная проблема с выбором строк из таблицы, где есть несколько возможностей для столбца VARCHAR в разделе where.

Здесь моя таблица (около 7 миллионов строк):

CREATE TABLE 'search_upload_detailed_results' (
  'id' bigint(20) NOT NULL AUTO_INCREMENT,
  'surId' bigint(20) DEFAULT NULL,
  'company' varchar(100) DEFAULT NULL,
  'country' varchar(45) DEFAULT NULL,
  'clei' varchar(100) DEFAULT NULL,
  'partNumber' varchar(100) DEFAULT NULL,
  'mfg' varchar(100) DEFAULT NULL,
  'cond' varchar(45) DEFAULT NULL,
  'price' float DEFAULT NULL,
  'qty' int(11) DEFAULT NULL,
  'age' int(11) DEFAULT NULL,
  'description' varchar(500) DEFAULT NULL,
  'status' varchar(45) DEFAULT NULL,
  'fileId' bigint(20) DEFAULT NULL,
  'nmId' bigint(20) DEFAULT NULL,
  'quoteRequested' tinyint(1) DEFAULT '0',
  PRIMARY KEY ('id'),
  KEY 'sudr.surId' ('surId'),
  KEY 'surd.clei' ('clei'),
  KEY 'surd.pn' ('partNumber'),
  KEY 'surd.fileId' ('fileId'),
  KEY 'surd.price' ('price')
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

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

Пример: номер детали "300-1231-932" может быть:

  • 300-1231-932
  • 3001231932
  • 300 1231 932

Такой простой выбор занимает 0.0008 секунд.

select avg(price) as price from search_upload_detailed_results where 
partNumber LIKE '3001231932%' and price > 0;

Но это не дает мне всех матчей, которые мне нужны. Поэтому я написал этот запрос.

select avg(price) as price from search_upload_detailed_results 
where REPLACE(REPLACE(partNumber,'-',''),' ','') LIKE REPLACE(REPLACE('3001231932%','-',''),' ','') and price > 0;

Это дает мне все правильные совпадения, но это очень медленно на 3,3 секунды.

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

select avg(price) as price from search_upload_detailed_results 
where price > 0 AND 
partNumber LIKE('300%') AND 
REPLACE(REPLACE(partNumber,'-',''),' ','') LIKE REPLACE(REPLACE('3001231932%','-',''),' ','');

Выполняется 0,4 секунды. Довольно быстро, но все равно может потребоваться много времени в многопользовательском поиске.

Я хотел бы получить его немного быстрее, но это насколько я могу получить. Есть ли другие способы оптимизации этого запроса?

UPDATE, чтобы показать объяснение для третьего запроса:

# id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, SIMPLE, search_upload_detailed_results, range, surd.pn,surd.price, surd.pn, 103, , 89670, Using where
  • 3
    Почему бы вам не канонизировать номера деталей в таблице, чтобы они всегда были в одном и том же формате?
  • 1
    почему бы вам не обновить этот столбец и избежать проблем в будущем?
Показать ещё 9 комментариев
Теги:
where-clause

4 ответа

1

Очевидным решением является просто сохранить номер детали без лишних символов в таблице. Затем удалите эти символы из пользовательского ввода и просто выполните простой WHERE partnumber = @input.

Если это невозможно, вы можете добавить это как дополнительный столбец. В MySQL 5.7 вы можете использовать созданный столбец; в более ранних версиях вы можете использовать триггер, который заполняет этот столбец.

  • 0
    Да, это очевидный ответ, но для рефакторинга кода и всех таблиц, содержащих partNumber, потребуется много времени. Я посмотрю в сгенерированный столбец возможность.
0

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

выберите avg (цена) как цена от search_upload_detailed_results, где partNumber REGEXP '^ 300 [-]? 1231 [-]? 932';

  • 0
    Я проверил REGEX, и производительность почти идентична.
0

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

Если есть несколько форматов, но не слишком много, и они хорошо известны (например, три, которые вы показали), тогда запрос может быть выполнен для ускорения работы путем явного предварительного расчета всех них и поиска любого из них.

select avg(price) as price from search_upload_detailed_results where 
partNumber IN ('300-1231-932', '3001231932', '300 1231 932')

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

  • 0
    К сожалению, реинжиниринг этого очень большого приложения сейчас отсутствует в картах. Ваше другое решение тоже не сработает, потому что номера деталей на этом не заканчиваются. Существует множество разных версий одной и той же детали (например, 300-1231-932-REV1, 300-1231-932-REV2, 300-1231-932-LN и т. Д.). Для поиска необходимо вернуть все эти детали.
0

Я хотел бы получить его немного быстрее, но это насколько я могу получить. Есть ли другие способы оптимизации этого запроса?

Как сказал Бармар, лучшим решением, если вам действительно нужна скорость (медленнее 3,3 с), является наличие в нем столбца с нетрансформированными данными (надеюсь, теперь стандартизован), что позволит вам запрашивать его, не указывая все разные типы номеров деталей.

Пример: номер детали "300-1231-932" может быть:

300-1231-932 || 3001231932 || 300 1231 932

Я думаю, вам стоит беспокоиться о презентации ваших данных, так как все эти разные форматы затруднят - можете ли вы отформатировать один стандарт (до того, как он достигнет БД)?

Здесь моя таблица (около 7 миллионов строк):

Не забывайте свой индекс!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню