Создание поиска клиента с помощью LEFT JOIN.Результаты исключаются, если во второй таблице не найдено совпадений - PullRequest
0 голосов
/ 28 января 2019

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

Вот запрос, встроенный в PHP:

$query = "SELECT *
            FROM customers
            LEFT OUTER JOIN phone_numbers ON customers.customer_id = phone_numbers.associated_customer
            LEFT OUTER JOIN email_addresses ON customers.customer_id = email_addresses.associated_customer
            WHERE first_name LIKE '%" . $_GET["fname"] . "%'
            AND last_name LIKE '%" . $_GET["lname"] . "%'
            AND phone_number LIKE '%" . $_GET["phone"] . "%'
            AND email_address LIKE '%" . $_GET["email"] . "%'";

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

Правильно ли я структурирую свои таблицыдля достижения этого отношения один ко многим?

РЕДАКТИРОВАТЬ: Пример данных для уточнения.

Таблица 1:

╔════════════════════════════════════════╗
║                customers               ║
╠═════════════╦════════════╦═════════════╣
║ customer_id ║ first_name ║ last_name   ║
╠═════════════╬════════════╬═════════════╣
║      1      ║ John       ║ Doe         ║
║      2      ║ John       ║ Wick        ║
║      3      ║ John       ║ Cena        ║
║      4      ║ John       ║ Krasinski   ║
║      5      ║ Jane       ║ Doe         ║
║      6      ║ Freddie    ║ Mercury     ║
╚═════════════╩════════════╩═════════════╝

Таблица 2:

╔══════════════════════════════════════════════════╗
║                   phone numbers                  ║
╠════════════════╦═══════════════╦═════════════════╣
║  phone_number  ║ associated_id ║ primary_contact ║
╠════════════════╬═══════════════╬═════════════════╣
║   5555555555   ║       2       ║        0        ║
║   6692216251   ║       2       ║        1        ║
║   2025550174   ║       3       ║        1        ║
╚════════════════╩═══════════════╩═════════════════╝

таблица 3:

╔═══════════════════════════════════════════════════╗
║                   email_addresses                 ║
╠═════════════════╦═══════════════╦═════════════════╣
║  email_address  ║ associated_id ║ primary_contact ║
╠═════════════════╬═══════════════╬═════════════════╣
║ jdoe@aol.com    ║       1       ║        1        ║
║ jwick@email.com ║       2       ║        1        ║
║ jwick@aol.com   ║       2       ║        0        ║
╚═════════════════╩═══════════════╩═════════════════╝

поисковый запрос:

first name: "John"
last name: ""
phone number: ""
email address: ""

Ожидается, что он вернет все совпадения для «Джона» в поле первого имени и тольковключите основные методы контакта:

╔═════════════╦════════════╦═════════════╦══════════════╦═════════════════╗
║ customer_id ║ first_name ║  last_name  ║ phone_number ║  email_address  ║
╠═════════════╬════════════╬═════════════╬══════════════╬═════════════════╣
║      1      ║ John       ║ Doe         ║              ║ jdoe@aol.com    ║
║      2      ║ John       ║ Wick        ║  6692216251  ║ jwick@email.com ║
║      3      ║ John       ║ Cena        ║  2025550174  ║                 ║
║      4      ║ John       ║ Krasinski   ║              ║                 ║
╚═════════════╩════════════╩═════════════╩══════════════╩═════════════════╝

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

╔═════════════╦════════════╦═════════════╦══════════════╦═════════════════╗
║ customer_id ║ first_name ║  last_name  ║ phone_number ║  email_address  ║
╠═════════════╬════════════╬═════════════╬══════════════╬═════════════════╣
║      2      ║ John       ║ Wick        ║  5555555555  ║ jwick@email.com ║
║      2      ║ John       ║ Wick        ║  6692216251  ║ jwick@email.com ║
║      2      ║ John       ║ Wick        ║  5555555555  ║ jwick@aol.com   ║
║      2      ║ John       ║ Wick        ║  6692216251  ║ jwick@aol.com   ║
╚═════════════╩════════════╩═════════════╩══════════════╩═════════════════╝

Однако предположим, что мой поисковый запрос выглядит следующим образом:

first name: "John"
last name: ""
phone number: "5555555555"
email address: ""

Ожидаемый результат будет:

╔═════════════╦════════════╦═════════════╦══════════════╦═════════════════╗
║ customer_id ║ first_name ║  last_name  ║ phone_number ║  email_address  ║
╠═════════════╬════════════╬═════════════╬══════════════╬═════════════════╣
║      2      ║ John       ║ Wick        ║  5555555555  ║ jwick@email.com ║
╚═════════════╩════════════╩═════════════╩══════════════╩═════════════════╝

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

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Не могли бы вы попробовать следующий запрос, который для каждой таблицы (телефона и электронной почты) учитывает два случая 1. Есть непустые критерии поиска, поэтому вы будете использовать его для фильтрации таблицы 2. Критерии поиска пусты, тогда вы толькоискать первичный_контакт.Ситуация, когда у клиента нет контакта, обрабатывается внешним соединением, но тогда необходимо разрешить, чтобы primary_contact был нулевым.

Ситуация, когда у клиента есть только вторичные контакты, не обрабатывается, ноне имеет смысла, если у вас есть контактный телефон (или адрес электронной почты), он должен быть основным.

Запрос намного длиннее, но он в два раза более логичен (один для электронной почты, один для телефона)

SELECT *
FROM customers c
LEFT OUTER JOIN phone_numbers p ON c.customer_id = p.associated_customer
LEFT OUTER JOIN email_addresses e ON c.customer_id = e.associated_customer
WHERE c.first_name LIKE '%" . $_GET["fname"] . "%'
AND c.last_name LIKE '%" . $_GET["lname"] . "%'
AND (( '%" . $_GET["phone"] . "%' <> '' and
        p.phone_number LIKE '%" . $_GET["phone"] . "%' )
     or
     ( '%" . $_GET["phone"] . "%' = '' and
        (p.primary_contact = 1 or p.primary_contact is null) ))
AND (( '%" . $_GET["email"] . "%' <> '' and
        e.email_address LIKE '%" . $_GET["email"] . "%' )
     or
     ( '%" . $_GET["email"] . "%' = '' and
        (e.primary_contact = 1 or e.primary_contact is null) ))
0 голосов
/ 28 января 2019

Ваш дизайн базы данных в порядке.

Ваши условия:

  • Если $_GET["phone"] <> '', то совпадают только телефонные строки, иначе только первичные контакты.
  • Если $_GET["email"] <> '' затем только соответствующие строки электронной почты, иначе только первичные контакты.

Это приводит к следующему запросу:

SELECT *
FROM customers c
LEFT JOIN phone_numbers p ON p.associated_customer = c.customer_id
LEFT JOIN email_addresses e ON e.associated_customer = c.customer_id
WHERE c.first_name LIKE :fname
  AND c.last_name LIKE :lname
  AND
  (
    (:phone <> '' AND p.phone_number LIKE '%' || :phone || '%')
    OR
    (:phone = '' AND (p.primary_contact = 1 OR p.primary_contact IS NULL))
  )
  AND
  (
    (:email <> '' AND e.email_address LIKE '%' || :email || '%')
    OR
    (:email = '' AND (e.primary_contact = 1 OR e.primary_contact IS NULL))
  )
ORDER BY c.first_name, c.last_name, p.phone_numbers, e.email_addresses;

(я заменил ваши команды GET переменными связывания дляудобочитаемость.)

В любом случае, при слабых шаблонах поиска, скажем, номер телефона, содержащий определенную цифру, и адрес электронной почты, содержащий определенное письмо, вы все равно можете получить декартово произведение:

Например:Джон Смит, телефоны: 123456, 234567, электронные письма: one@company.com, two@company.com.Поиск: Джон Смит, телефон содержит 2, электронная почта содержит о.Результат:

fname | lname | phone  | email
------+-------+--------+----------------
John  | Smith | 123456 | one@company.com
John  | Smith | 123456 | two@company.com
John  | Smith | 234567 | one@company.com
John  | Smith | 234567 | two@company.com

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

fname | lname | phones         | emails
------+-------+----------------+---------------------------------
John  | Smith | 123456, 234567 | one@company.com, two@company.com
...