#sql #postgresql
Есть строка фильтра. Пользователь вводит в нее часть имени или фамилии или отчества, отображается список частичных совпадений, пользователь вводит польностью фимилию Иванов, отображаются Ивановы, далее он ставит пробел и вводит С, поиск должен найти всех Ивановых, чьи имена или отчества начинаются на С. В список не должны попадать фамилии на С., в БД ФИО хранятся отдельными полями. Как привильнее всего построить текст запроса? Я разбивал строку на слова и для каждого слова я добавлял условия, получалось следующее: SELECT * FROM users WHERE (name ILIKE word1 OR surname ILIKE word1 OR patronymic ILIKE word1) AND (name ILIKE word2 OR surname ILIKE word2 OR patronymic ILIKE word2) AND (name ILIKE word3 OR surname ILIKE word3 OR patronymic ILIKE word3)
Ответы
Ответ 1
А почему вы не хотите фамилии на С ? Если считать, что пользователь вводит сначала имя Иван, а потом вводит букву С было бы логично показать Ивана Сидорова, а Сидора Иванова наверное не надо. А если он вводит две буквы, то уже начинать искать ? как ему найти "Гевара Че" ? Предлагаю такую логику: пока слово одно - ищем его во всех трех полях по частичному совпадению. Но возможно надо ограничить минимальный размер слова, если оно из одной буквы, то боюсь результатов слишком много будет. Если слов более одного - то первое слово ищем во всех полях по точному совпадению, а остальные слова - то частичному совпадению. Запрос, реализующий вышеуказанную логику: select distinct users.* from ( select split_part(S,' ',N.column1) w1,split_part(S,' ',N.column2) w2, split_part(S,' ',N.column3) w3, substr('%%',2-N.column1/2) p1,substr('%%',2-N.column2/2) p2, substr('%%',2-N.column3/2) p3,(rtrim(S) like '% _')::int+1 np from (values(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,2,1),(3,1,2)) N, (select Cast('Пушкин А С' as varchar) S) S ) A join users on name LIKE w1||substr(p1,np,1) and surname LIKE w2||substr(p2,np,1) and patronymic LIKE w3||substr(p3,np,1) Единственный параметр передаваемый в запрос - ваша строка "Пушкин С А". Перед передачей строки из нее надо удалить лишние пробелы, т.е. слова должны быть разделены строго одним пробелом. Если нужен регистронезависимый поиск - приведите строку к нужному регистру в самом запросе По работе запроса: цифры в подзапросе N - весь набор расположения слов. w1-w3 - выделенные компоненты из строки, согласно номерам в N. p1-p3 - один % для первого слова строки, и %% для остальных. np - признак поиска полного соответствия (когда слов более 1). substr в самом низу по этому признаку берут или не берут % для like.Ответ 2
Например так: SELECT * FROM Table WHERE UPPER(' '||STR||' ') LIKE '% '||UPPER(FIO_1)||' %' AND UPPER(' '||STR||' ') LIKE '% '||UPPER(FIO_2)||' %' AND UPPER(' '||STR||' ') LIKE '% '||UPPER(FIO_3)||' %' Где STR - строка, в которой есть ФИО. FIO_1; FIO_2; FIO_3 - соответственно имя фамилия отчество. upd: Объединил два ответа в один: У вас есть 6 вариантов сравнений. 3 паттерна и 3 столбца. Причем один паттерн нельзя применять к двум разным столбцам, что усложняет. Это число размещений. Через Or будет работать медленно. Можно попробовать через UNION: SELECT Users.* FROM( SELECT Id FROM users WHERE name LIKE word1||'%' AND surname LIKE word2||'%' AND patronymic LIKE word3||'%' UNION SELECT Id FROM users WHERE name LIKE word1||'%' AND surname LIKE word3||'%' AND patronymic LIKE word2||'%' UNION SELECT Id FROM users WHERE name LIKE word2||'%' AND surname LIKE word1||'%' AND patronymic LIKE word3||'%' UNION SELECT Id FROM users WHERE name LIKE word2||'%' AND surname LIKE word3||'%' AND patronymic LIKE word1||'%' UNION SELECT Id FROM users WHERE name LIKE word3||'%' AND surname LIKE word1||'%' AND patronymic LIKE word2||'%' UNION SELECT Id FROM users WHERE name LIKE word3||'%' AND surname LIKE word2||'%' AND patronymic LIKE word1||'%' )T JOIN Users ON Users.Id = T.Id Выглядит некрасиво. Можно ещё через JOIN: SELECT Users.* FROM Users JOIN (VALUES (word1,word2,word3), (word1,word3,word1), (word2,word1,word3), (word2,word3,word1), (word3,word1,word2), (word3,word2,word1) )T(word1,word2,word3) ON name LIKE word3||'%' AND surname LIKE word2||'%' AND patronymic LIKE word1||'%' Однако попытаться учесть все 6 вариантов сравнения как-то подругому ещё хуже. Другое дело, нужны ли вам все 6 вариантов? И не забываем добавить '%' в конец паттерна поиска. Но если вам нужны точные соответствия, тогда можно сделать JOIN таблички (name, surname, patronymic) с табличкой (word1, word2, word3) Либо по LIKE либо по равенству. Либо комбинируя. Комбинировать можно, если например вам нужно соединить последнее слово в запросе по LIKE, а остальные по равенству.
Комментариев нет:
Отправить комментарий