Как искать в 5 разных полях со всеми возможными вариантами в произвольном порядке в MySQL, используя LIKE - PullRequest
3 голосов
/ 01 февраля 2010

У меня есть база данных, которая содержит 5 полей: имя, адрес, idcard, город, улица

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

Например, если я введу в поле поиска строку «Джон скатить улицу», вы получите все записи, которых зовут Джон Доу, и они живут на улице, известной как Скейт-стрит.

Кроме того, обратите внимание, что имя хранится в одном поле в следующем порядке: ИМЯ ФАМИЛИЯ (я не могу изменить это, потому что это обязательно, поэтому, пожалуйста, не говорите мне, что это лучше изменить ... )). Очевидно, мне бы хотелось, чтобы имя можно было вводить в любом порядке, будь то ИМЯ ФАМИЛИЯ или ИМЯ ФАМИЛИЯ.

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

Они будут приняты:

name surname idcard town street
surname name idcard town street
name surname town idcard street
surname name address street idcard

Они не будут приняты:

idcard town name surname street
town name surname idcard

по той простой причине, что мы слишком усложним запрос (из-за причины имени / фамилии и их нахождения в одном поле)

Если есть способ, как последнее возможно без особых осложнений, я бы тоже хотел это услышать.

Буду признателен за помощь в этом вопросе

Большое спасибо заранее

Ответы [ 3 ]

4 голосов
/ 01 февраля 2010

Вид поиска, за которым вы, похоже, следите, на самом деле не самая лучшая точка для СУБД, таких как MySQL. А указание допустимых форматов поиска обычно не годится для удобства использования, если только это не довольно специфический домен.

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

SELECT * FROM Users
WHERE (FirstName LIKE "%Persons%"
OR LastName LIKE "%Persons%"
OR Address LIKE "%Persons%")
AND (FirstName LIKE "%Name%"
OR LastName LIKE "%Name%"
OR Address LIKE "%Name%")
AND (FirstName LIKE "%Fake%"
OR LastName LIKE "%Fake%"
OR Address LIKE "%Fake%")
AND (FirstName LIKE "%Street%"
OR LastName LIKE "%Street%"
OR Address LIKE "%Street%")

Это должен найти любой участник, чьи данные совпадают с указанными. Но не очень элегантно и будет только хуже с более длинными запросами и большим количеством полей. Это также невероятно неэффективно и будет быстро бороться, поскольку таблица становится длиннее - она ​​не может использовать индексы. Это также не поможет вам получить «лучшие» совпадения наверху (если результатов много)

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

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

Правильное решение зависит от ваших требований (как всегда), но это некоторые идеи, которые стоит изучить.

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

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

Регулярное выражение может выглядеть примерно так

preg_match_all('/(\S+)*\s+/im', $input, $value, PREG_PATTERN_ORDER);
for ($i = 0; $i < count($value[0]); $i++) {
    # Matched text = $result[0][$i];
}

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

Суть этой идеи состоит в том, чтобы объединить таблицу ввода в каждом столбце, который вы хотите найти. Предложение where учитывает тот факт, что входные значения находятся по крайней мере в количестве столбцов, поскольку есть входные значения.

DECLARE @Table TABLE (Name VARCHAR(20), Surname VARCHAR(20), Address VARCHAR(20), IDCard VARCHAR(20), Town VARCHAR(20), Street VARCHAR(20))
DECLARE @Inputs TABLE (Value VARCHAR(32))

INSERT INTO @Table VALUES ('Doe', 'John', 'Dontknow', 'Dontknow', 'US', 'Skate')
INSERT INTO @Inputs VALUES ('%John%')
INSERT INTO @Inputs VALUES ('%Doe%')
INSERT INTO @Inputs VALUES ('%Skate%')

SELECT  t.*
FROM    @Table t
        LEFT OUTER JOIN @Inputs i_name ON t.Name LIKE i_name.Value     
        LEFT OUTER JOIN @Inputs i_surname ON t.SurName LIKE i_surname.Value        
        LEFT OUTER JOIN @Inputs i_address ON t.Address LIKE i_address.Value
        LEFT OUTER JOIN @Inputs i_idcard ON t.IDCard LIKE i_idcard.Value
        LEFT OUTER JOIN @Inputs i_town ON t.Town LIKE i_town.Value
        LEFT OUTER JOIN @Inputs i_street ON t.Street LIKE i_street.Value
        CROSS APPLY (SELECT inputCount = COUNT(*) FROM @Inputs) cnt
WHERE   cnt.inputCount <= 
          CASE WHEN i_name.Value IS NULL THEN 0 ELSE 1 END
          + CASE WHEN i_surname.Value IS NULL THEN 0 ELSE 1 END
          + CASE WHEN i_address.Value IS NULL THEN 0 ELSE 1 END
          + CASE WHEN i_idcard.Value IS NULL THEN 0 ELSE 1 END
          + CASE WHEN i_town.Value IS NULL THEN 0 ELSE 1 END
          + CASE WHEN i_street.Value IS NULL THEN 0 ELSE 1 END
0 голосов
/ 01 февраля 2010

Это менее чем идеально, но так же, как и ваш дизайн:

Токенируйте вводный параметр "имя, фамилия, idcard town street", затем соедините поля из БД и выполните серию подобных или:

psudocode
---------
where
name+address+idcard+town+street like %input_token1% or
name+address+idcard+town+street like %input_token2% or
...
name+address+idcard+town+street like %input_token5%
...