Запрос на поиск людей с несколькими именами - PullRequest
0 голосов
/ 13 июля 2020

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

Это пример регистрации в базе данных:

username first0 first1 first2 first3 first4
(email)  Lisa   John   Frank  Sam    Smith

Прямо сейчас мой запрос выглядит так:

SELECT * FROM NAMES
WHERE (
    `names`.`first0` IN ("Lisa", "Sam", "")
    AND `names`.`first1` IN ("Lisa", "Sam", "")
    AND `names`.`first2` IN ("Lisa", "Sam", "")
    AND `names`.`first3` IN ("Lisa", "Sam", "")
    AND `names`.`first4` IN ("Lisa", "Sam", "")

** Подставьте точные имена для данных, введенных в форму поиска.

Этот запрос не работает, потому что first2 = Frank, что не входит в список. Если человек не знает каждого имени, введенного в базу данных, все ломается.

Однако я не думаю, что хочу использовать операторы ИЛИ, потому что мне не нужны все результаты, в которых имя соответствует Lisa, которое будет возвращено, если кто-то укажет Lisa как одно из имен.

Кто-нибудь знает, как я мог go сделать это?

Ответы [ 3 ]

2 голосов
/ 13 июля 2020

Вы здесь излишне скованы. Вместо этого рассмотрим:

User
UserId, OtherData

UserNames
UserId, Name, Kind

Данные вроде:

UserId, OtherData
1, Blahblah

UserId, Name, Type
1, Lisa, First 
1, Jane, Middle
1, Smith, Last   
1, Leese, First
1, Jones, Last

Лиза Джейн Смит, также известная как Лиз, вышла замуж и сменила имя на Лизу Джейн Джонс

Если вы представляете пользователь веб-сайта с помощью команды «Введите любое количество имен и укажите их тип», затем вы можете запустить запрос, который найдет идентификаторы пользователей, в которых указаны все типы имен.

Это может быть так же просто, как динамическое присоединение или выполнение условное агрегирование

Например, если пользователь вводит «Leese» в качестве имени и «Jones» в качестве фамилии, вы можете:

SELECT UserID FROM UserNames 
WHERE 
  (name = 'Leese' AND type = 'First') OR
  (name = 'Jones' AND type = 'Last')
GROUP BY
  UserID 
HAVING count(*) = 2 --2 is the number of names entered, leese and jones.  If your where clause had 5 names, this would be 5

Или вы можете использовать подход объединения (и это для if веб-пользователь вводит 3 имени / вида):

SELECT u1.UserID 
FROM
  UserNames u1 
  INNER JOIN Usernames u2 ON u1.UserID = u2.UserID
  INNER JOIN Usernames u3 ON u1.UserID = u3.UserID
WHERE 
  u1.name = 'Leese' AND u1.type = 'First' AND
  u2.name = 'Jones' AND u2.type = 'Last' AND
  u3.name = 'Smith' AND u3.type = 'Last' AND
  

Когда вы go формируете свои динамические c запросы, не объединяйте значения в SQL. Тот факт, что вы создаете динамический c запрос, не является оправданием для того, чтобы выбросить параметризацию из окна. Вы создаете SQL, объединяя вместе строку SQL с заполнителями параметров, а затем даете значения параметров отдельно

Псевдокод (я не знаю php) на языке, который интерполирует свои строки с {заполнители}:

string fromclause = ""
string whereclause = ""
for i = 0 to webrequest.form.values.length - 1

  string name = webrequest.form.values[i].name
  string type = webrequest.form.values[i].type


  if i == 0 then
    fromclause = fromclause + "Usernames u{i}"
  else
    fromclause = fromclause + " INNER JOIN Usernames u{i} ON u1.UserID = u{i}.UserID

  whereclause = whereclause + " u{i}.Name = @paramName{i} AND u{i}.type = @paramType{i}"

  sqlcommand.parameters.add("@paramName{i}", name)
  sqlcommand.parameters.add("@paramType{i}", type)

next i

sqlcommand.commandtext = "SELECT UserID FROM {fromclause} WHERE {whereclause}"

Вы завершаете sh командой sql, которая параметризована, и все параметры имеют заданные значения ..

1 голос
/ 13 июля 2020

Нормализованная схема может выглядеть так ...

id username first
 1 (email)  Lisa   
 2 (email)  John   
 3 (email)  Frank  
 4 (email)  Sam    
 5 (email)  Smith
0 голосов
/ 13 июля 2020

С вашей текущей схемой такой оператор, вероятно, будет работать:

SELECT * FROM `names`
WHERE (
    `names`.`first0` = "Lisa" OR
    `names`.`first1` = "Lisa" OR
    `names`.`first2` = "Lisa" OR
    `names`.`first3` = "Lisa" OR
    `names`.`first4` = "Lisa"
) AND (
    `names`.`first0` = "Sam" OR
    `names`.`first1` = "Sam" OR
    `names`.`first2` = "Sam" OR
    `names`.`first3` = "Sam" OR
    `names`.`first4` = "Sam"
)

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

CREATE old_firstnames (
  username VARCHAR(255),
  firstname VARCHAR(255),
  timestamp DATETIME,
  PRIMARY KEY (username, firstname, timestamp)
)

Тогда ваши данные, вероятно, будут:

username firstname timestamp
(email)  Lisa      1998-02-01
(email)  John      2001-03-02
(email)  Frank     2006-01-04
(email)  Sam       2009-11-13
(email)  Smith     2018-09-01

И вы можете искать своего пользователя по:

SELECT * FROM old_firstnames n1
LEFT JOIN old_firstname n2 ON (n1.username = n2.username)
WHERE n1.firstname = "Lisa" AND n2.firstname = "Sam"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...