简体   繁体   English

使用 LEFT JOIN 创建客户搜索。 如果在第二个表中找不到匹配项,则排除结果

[英]Creating customer search using LEFT JOIN. Results excluded if no match found in second table

I'm trying to create a way to search through a database of customers, where some pieces of data like phone numbers and emails have a one-to-many relationship.我正在尝试创建一种方法来搜索客户数据库,其中电话号码和电子邮件等某些数据具有一对多关系。 I have a table that has a customer ID (unique, autonumber), a first name, and a last name.我有一个表,其中包含一个客户 ID(唯一的、自动编号)、一个名字和一个姓氏。 I have a second table containing a phone number, and the customer ID that that phone number is associated with.我有一个包含电话号码的第二个表,以及与该电话号码相关联的客户 ID。 I have a third table containing an email address, and the customer ID that that email address is associated with.我有一个包含电子邮件地址的第三个表,以及与该电子邮件地址相关联的客户 ID。

Here is the query built in PHP:这是用 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"] . "%'";

With this, the customer I searched for only comes up if they have an phone number and email address.有了这个,我搜索的客户只有在他们有电话号码和电子邮件地址时才会出现。 I also get duplicate results from the first table if the customer has multiple phone numbers or emails, so if the customer has 2 phone numbers and 2 emails, I would get 4 results back.如果客户有多个电话号码或电子邮件,我也会从第一个表中得到重复的结果,所以如果客户有 2 个电话号码和 2 个电子邮件,我会得到 4 个结果。

Am I structuring my tables correctly for to achieve this one-to-many relationship?我是否正确构建了我的表格以实现这种一对多关系?

EDIT: Sample data for clarification.编辑:用于澄清的示例数据。

table 1:表格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     ║
╚═════════════╩════════════╩═════════════╝

table 2:表2:

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

table 3:表3:

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

search query:搜索查询:

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

The expected result is for it to return all matches for "John" in the first name field, and only include the primary methods of contact:预期的结果是它返回名字字段中“John”的所有匹配项,并且只包括主要的联系方式:

╔═════════════╦════════════╦═════════════╦══════════════╦═════════════════╗
║ 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   ║              ║                 ║
╚═════════════╩════════════╩═════════════╩══════════════╩═════════════════╝

The actual result is that all results that do not have an associated phone number AND email address are excluded, and a duplicate is included for each phone number and each email address:实际结果是所有没有关联电话号码和电子邮件地址的结果都被排除在外,并且每个电话号码和每个电子邮件地址都包含一个重复项:

╔═════════════╦════════════╦═════════════╦══════════════╦═════════════════╗
║ 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   ║
╚═════════════╩════════════╩═════════════╩══════════════╩═════════════════╝

However, let's say my search query is the following:但是,假设我的搜索查询如下:

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

The expected result would be:预期的结果是:

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

I am aware of SQL injection attacks.我知道 SQL 注入攻击。 I will have measures in place to prevent them, but this will be an internal system, so I'm not that worried about it.我会采取措施来防止它们,但这将是一个内部系统,所以我并不担心。

Could you try the following query which for each table (phone and email) consider two cases 1. there's a non empty search criteria so you'll use it to filter the table 2. the search criteria is empty, you then only look for primary_contact.您能否尝试以下查询,其中每个表(电话和电子邮件)考虑两种情况 1. 有一个非空搜索条件,因此您将使用它来过滤表 2. 搜索条件为空,然后您只查找 primary_contact . The situation where the client doesn't have a contact is handled with the outer join but then you need to allow for primary_contact to be null.客户端没有联系人的情况由外部联接处理,但您需要允许 primary_contact 为空。

The situation where the customer only has only secondary contacts is not handled but it doesn't make sense if you have a contact phone (or email) one has to be the primary one.客户只有次要联系人的情况没有得到处理,但如果您有一个联系电话(或电子邮件),那么联系电话(或电子邮件)必须是主要联系人,这没有意义。

The query is much longer but it's twice the same logic (one for email one for phone)查询要长得多,但它的逻辑是相同的两倍(一个用于电子邮件,一个用于电话)

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) ))

Your database design is fine.你的数据库设计很好。

Your conditions:您的条件:

  • If $_GET["phone"] <> '' then only matching phone rows, else only primary contacts.如果$_GET["phone"] <> ''则只匹配电话行,否则只匹配主要联系人。
  • If $_GET["email"] <> '' then only matching email rows, else only primary contacts.如果$_GET["email"] <> ''则仅匹配电子邮件行,否则仅匹配主要联系人。

This leads to the following query:这导致以下查询:

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;

(I've replaced your GET commands with bind variables for readability.) (为了可读性,我已经用绑定变量替换了你的 GET 命令。)

Anyway, with weak search patterns, say the phone number containing a certain digit and and the email address containing a certain letter, you could still get a cartesian product:无论如何,使用弱搜索模式,比如电话号码包含某个数字和电子邮件地址包含某个字母,你仍然可以获得笛卡尔积:

Eg: John Smith, phones: 123456, 234567, emails: one@company.com, two@company.com.例如:John Smith,电话:123456、234567,电子邮件:one@company.com、two@company.com。 Search: John Smith, phone contains 2, email contains o.搜索:John Smith,电话包含 2,电子邮件包含 o。 Result:结果:

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

You may want to consider this somehow, eg use string aggregation, so as to get the following result instead:您可能想以某种方式考虑这一点,例如使用字符串聚合,以获得以下结果:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM