В моей базе данных есть таблица author, которая состоит из 4 столбцов:

  • AuthorID
  • Имя
  • второе имя
  • фамилия

Например, пользователь ищет Эдгара Аллана По. В нашей таблице Эдгар Аллан По сохраняется как: firstName - Эдгар, middleName - Аллан и lastName - По. Этот запрос довольно прост. Но как написать запрос, который соответствует не только Эдгару Аллану По, но и По Аллану Эдгару, Эдгару По, Аллану По, Эдгару Аллану, Аллану Эдгару По, не написав все эти возможные комбинации самостоятельно? Также, когда пользователь ищет, он / она ищет как «Эдгар Аллан По» или «По Аллан Эдгар» в целом, а не в отдельных полях.

5
Xty83a 8 Май 2017 в 04:30

3 ответа

Лучший ответ

Пожалуйста, попробуйте следующее ...

DROP PROCEDURE IF EXISTS SimilarNames;
DELIMITER //
    CREATE PROCEDURE SimilarNames( authorFullName VARCHAR( 250 ) )
    BEGIN
        SET @authorFullNameCommad = CONCAT( '\'',
                                            REPLACE( authorFullName,
                                                     ' ',
                                                     '\', \'' ),
                                            '\'' );

        SET @selectStatementString := CONCAT( "SELECT authorID,",
                                              "       firstName,",
                                              "       middleName,",
                                              "       lastName ",
                                              "FROM author ",
                                              "WHERE ( ( firstName IN ( ",
                                              @authorFullNameCommad,
                                              " ) ) + ( middleName IN ( ",
                                              @authorFullNameCommad,
                                              " ) ) + ( lastName IN ( ",
                                              @authorFullNameCommad,
                                              " ) ) ) >=2;" );

        PREPARE selectStatement FROM @selectStatementString;
        EXECUTE selectStatement;
        DEALLOCATE PREPARE selectStatement;
   END //
DELIMITER ;
CALL SimilarNames( 'Edgar Allan Poe' );

Это решение начинается с создания PROCEDURE с именем SimilarNames (после того, как DROP пропингует любые существующие версии PROCEDURE). Это PROCEDURE сохраняет переданное ему имя (например, 'Edgar Allan Poe') в переменной параметра authorFullName.

После начала PROCEDURE начинается с преобразования строки, такой как Edgar Allan Poe, в 'Edgar', 'Allan', 'Poe' и сохранения ее в переменной @authorFullNameCommad.

Функция CONCAT() затем используется для формирования текста оператора SQL, который будет давать наши результаты. Где authorFullName равен Edgar Allan Poe, следующее выражение создается и сохраняется в @selectStatementString ...

SELECT authorID,
       firstName,
       middleName,
       lastName
FROM author
WHERE ( ( firstName IN ( 'Edgar', 'Allan', 'Poe' ) ) + ( middleName IN ( 'Edgar', 'Allan', 'Poe' ) ) + ( lastName IN ( 'Edgar', 'Allan', 'Poe' ) ) ) >=2;

Тогда оператор SQL имеет вид PREPARE d и EXECUTE d, что создает желаемый список при вызове PROCEDURE, что можно сделать с помощью ...

CALL SimilarNames( 'Edgar Allan Poe' );

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

CALL SimilarNames( 'Edgar Allan Poe' );
CALL SimilarNames( 'James Tiberius Kirk' );

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

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

CREATE TABLE author
(
    authorID     INT   NOT NULL AUTO_INCREMENT,
    firstName    VARCHAR( 50 ),
    middleName   VARCHAR( 50 ),
    lastName     VARCHAR( 50 ),
    PRIMARY KEY ( authorID )
);
INSERT INTO author ( firstName,
                     middleName,
                     lastName )
VALUES ( 'Edgar', 'Allan', 'Poe' ),
       ( 'Poe', 'Allan', 'Edgar' ),
       ( 'Edgar', 'Poe', '' ),
       ( 'Edgar', '', 'Poe' ),
       ( '', 'Edgar', 'Poe' ),
       ( 'Allan', 'Poe', '' ),
       ( 'Edgar', 'Allan', '' ),
       ( 'Allan', 'Edgar', 'Poe' ),
       ( 'Edgar', 'Allan', 'Allan' ),
       ( 'James', 'Tiberius', 'Kirk' ),
       ( 'Karl', 'Ignatius', 'von Bach' ),
       ( 'Edgar', 'Poe', 'xyz' ),
       ( 'Allanah', 'Poelsen', '' );

Результаты оказались такими, как я ожидал.

Если у вас есть какие-либо вопросы или комментарии, пожалуйста, не стесняйтесь оставлять комментарии соответственно.

Дальнейшее чтение

https://dev.mysql.com/doc/refman/5.7/ ru / call.html (в выражении MySQL CALL)

https://dev.mysql.com/doc/ refman / 5.7 / ru / string-functions.html # function_concat (в функции MySQL CONCAT())

https://dev.mysql.com/doc/refman/ 5.7 / en / create -ystem.html (в выражении MySQL CREATE PROCEDURE)

https://dev.mysql.com/doc/refman/ 5.7 / en / deallocate-prepare.html (в выражении MySQL DEALLOCATE)

https://dev.mysql.com/doc/ refman / 5.7 / ru /ved-Programs-Defining.html (по команде MySQL DELIMITER)

https://dev.mysql.com/doc/refman/ 5.7 / en / drop -ystem.html (в выражении MySQL DROP PROCEDURE)

https://dev.mysql.com/doc/refman/5.7/ ru / execute.html (в операторе MySQL EXECUTE)

https://dev.mysql.com/doc/ refman / 5.7 / ru / сравнение-operator.html # function_in (в операторе MySQL IN)

https://dev.mysql.com/doc/refman/5.7/ ru / prepare.html (в операторе MySQL PREPARE)

https://dev.mysql.com/doc/ refman / 5.7 / ru / string-functions.html # function_replace (в функции MySQL REPLACE())

https://dev.mysql.com/doc/refman/ 5.7 / en / set-Statement.html (в выражении MySQL SET)

2
toonice 8 Май 2017 в 15:44

Для общей СУБД, если вы намерены просто упростить ваши запросы, одним из способов будет добавление отдельного вычисляемого столбца в таблицу авторов, который запускается при вставке и обновлении.

Другими словами, имейте столбец с именем searchFullName и установите его в:

<space><firstName><space><secondName><space><lastName><space>

Тогда ваш запрос становится (относительно) простым:

select something from author
where searchFullName like '% Poe %'
  and searchFullName like '% Edgar %'

Это не будет ослепительно быстро для больших столов, но оно должно достичь того, что вы хотите.

0
paxdiablo 8 Май 2017 в 01:37

В любой базе данных вы можете сделать что-то вроде этого:

where firstName in ('Edgar', 'Allan', 'Poe') and
      middleName in ('Edgar', 'Allan', 'Poe') and
      middleName <> firstName and
      lastname in ('Edgar', 'Allan', 'Poe') and
      lastname not in (firstname, middleName)

Это на самом деле довольно легко распространить на большее количество имен, если хотите - при условии, что имена различны, как в вашем примере (если вы хотите разрешить авторам с дублированными именами, просто удалите строки 3 и 5 из вышеприведенного запроса).

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

3
paxdiablo 8 Май 2017 в 01:44