与LEFT JOINS相关的MySQL子查询

时间:2015-04-29 16:13:06

标签: mysql subquery correlated-subquery

我被赋予了从数据库中提取客户信息的任务,我被困在最后一部分!我希望我的解释足以描述我的问题和尝试。

目标:每位客户使用所有电话号码返回一行

问题:每个客户可能有很多电话号码

Related Diagram:

尝试:

SUBQUERY:获得"子查询返回超过1行"错误。这对我来说很有意义,但我不能为每个客户插入一个WHERE语句

SELECT customer.Name, address.Street, address.City, address.State, address.Zip, customer.Contact, email.Address, customer.CTYPE,
(SELECT telephone.Number 
    FROM customer
    LEFT JOIN customertelephone ON customer.CustomerID = customertelephone.CustomerID
    LEFT JOIN telephone ON customertelephone.TelephoneID = telephone.TelephoneID 
    WHERE telephone.Type = "Main") as MainPhone
FROM `customer`
LEFT JOIN customeraddress ON customer.CustomerID = customeraddress.CustomerID
LEFT JOIN address ON customeraddress.AddressID = address.AddressID
LEFT JOIN customeremail ON customer.CustomerID = customeremail.CustomerID
LEFT JOIN email ON customeremail.EmailID = email.EMailID
LIMIT 100

LEFT JOIN:查询返回一行/ customer / number,但我需要一行中的每个数字。

SELECT customer.Name, address.Street, address.City, address.State, address.Zip, customer.Contact, email.Address, customer.CTYPE, telephone.Number
FROM `customer`
LEFT JOIN customeraddress ON customer.CustomerID = customeraddress.CustomerID
LEFT JOIN address ON customeraddress.AddressID = address.AddressID
LEFT JOIN customeremail ON customer.CustomerID = customeremail.CustomerID
LEFT JOIN email ON customeremail.EmailID = email.EMailID
LEFT JOIN customertelephone ON customer.CustomerID = customertelephone.CustomerID
LEFT JOIN telephone ON customertelephone.TelephoneID = telephone.TelephoneID 
LIMIT 100

GROUP BY:查询正确地为每个客户返回一行,但只返回第一个数字。

SELECT customer.Name, address.Street, address.City, address.State, address.Zip, customer.Contact, email.Address, customer.CTYPE, telephone.Number
FROM `customer`
LEFT JOIN customeraddress ON customer.CustomerID = customeraddress.CustomerID
LEFT JOIN address ON customeraddress.AddressID = address.AddressID
LEFT JOIN customeremail ON customer.CustomerID = customeremail.CustomerID
LEFT JOIN email ON customeremail.EmailID = email.EMailID
LEFT JOIN customertelephone ON customer.CustomerID = customertelephone.CustomerID
LEFT JOIN telephone ON customertelephone.TelephoneID = telephone.TelephoneID
GROUP BY customer.CustomerID
LIMIT 100

如何为每位客户返回一行,每行只显示一行电话号码?

编辑:

我刚刚收到了一些非常棒的帮助:group_concat确实有奇迹!现在我正在尝试正确格式化查询返回。

目标:将GROUP_CONCAT返回的值分隔为新字段

当前SQL代码:

SELECT customer.Name, address.Street, address.City, address.State, address.Zip, customer.Contact, email.Address, customer.CTYPE, GROUP_CONCAT(telephone.Number) as TelephoneNumbers, GROUP_CONCAT(telephone.Type) as Types
FROM `customer`
LEFT JOIN customeraddress ON customer.CustomerID = customeraddress.CustomerID
LEFT JOIN address ON customeraddress.AddressID = address.AddressID
LEFT JOIN customeremail ON customer.CustomerID = customeremail.CustomerID
LEFT JOIN email ON customeremail.EmailID = email.EMailID
LEFT JOIN customertelephone ON customer.CustomerID = customertelephone.CustomerID
LEFT JOIN telephone ON customertelephone.TelephoneID = telephone.TelephoneID
GROUP BY customer.CustomerID

GROUP_CONACT的当前结果:

TelephoneNumbers                 Type
321-000-0000,321-000-0000      Main, Fax
321-001-0000                   Mobile

我想要实现的目标:

    Main           Fax            Mobile
321-000-0000   321-000-0000        NULL
    NULL           NULL        321-001-0000

尝试: GROUP_CONCAT中的WHERE语句,抛出错误

GROUP_CONCAT(telephone.Number WHERE GROUP_CONCAT(telephone.Type) = "MAIN") as Main

甚至可以实现这个目标吗?

编辑:

最终代码(谢谢user4829935!):

SELECT customer.Name, address.Street, address.City, address.State, address.Zip, customer.Contact, email.Address, customer.CTYPE, GROUP_CONCAT(tmain.Number) as Main, GROUP_CONCAT(tmobile.Number) as Mobile, GROUP_CONCAT(tfax.Number) as Fax
FROM `customer`
LEFT JOIN customeraddress ON customer.CustomerID = customeraddress.CustomerID
LEFT JOIN address ON customeraddress.AddressID = address.AddressID
LEFT JOIN customeremail ON customer.CustomerID = customeremail.CustomerID
LEFT JOIN email ON customeremail.EmailID = email.EMailID
LEFT JOIN customertelephone ON customer.CustomerID = customertelephone.CustomerID
LEFT JOIN telephone as tmain ON customertelephone.TelephoneID = tmain.TelephoneID AND tmain.type = 'Main'
LEFT JOIN telephone as tmobile ON customertelephone.TelephoneID = tmobile.TelephoneID AND tmobile.type = 'Mobile'
LEFT JOIN telephone as tfax ON customertelephone.TelephoneID = tfax.TelephoneID AND tfax.type = 'Fax'
GROUP BY customer.CustomerID

2 个答案:

答案 0 :(得分:2)

试试这个:

SELECT customer.Name, address.Street, address.City, address.State, address.Zip, customer.Contact, email.Address, customer.CTYPE, GROUP_CONCAT(telephone.Number)
FROM `customer`
LEFT JOIN customeraddress ON customer.CustomerID = customeraddress.CustomerID
LEFT JOIN address ON customeraddress.AddressID = address.AddressID
LEFT JOIN customeremail ON customer.CustomerID = customeremail.CustomerID
LEFT JOIN email ON customeremail.EmailID = email.EMailID
LEFT JOIN customertelephone ON customer.CustomerID = customertelephone.CustomerID
LEFT JOIN telephone ON customertelephone.TelephoneID = telephone.TelephoneID
GROUP BY customer.CustomerID

您将以逗号分隔电话号码。

编辑:

将不同的数字作为不同的字段,例如:

name      street      city        state  zip    ... main_phone   fax
John Doe  123 Main St Springfield CA     99999      123-555-5555 123-555-5556

您需要提前知道可能的电话号码类型,并将其编码到查询中。这就是你想要的吗?

这就像是:

SELECT customer.Name, address.Street, address.City, address.State, address.Zip, customer.Contact, email.Address, customer.CTYPE, GROUP_CONCAT(tm.Number) as mainTelephone, GROUP_CONCAT(tf.Number) as fax
FROM `customer`
LEFT JOIN customeraddress ON customer.CustomerID = customeraddress.CustomerID
LEFT JOIN address ON customeraddress.AddressID = address.AddressID
LEFT JOIN customeremail ON customer.CustomerID = customeremail.CustomerID
LEFT JOIN email ON customeremail.EmailID = email.EMailID
LEFT JOIN customertelephone ON customer.CustomerID = customertelephone.CustomerID
LEFT JOIN telephone as tm ON customertelephone.TelephoneID = tm.TelephoneID AND tm.type = 'mainTelephone'
LEFT JOIN telephone as tf ON customertelephone.TelephoneID = tf.TelephoneID AND tf.type = 'fax'
GROUP BY customer.CustomerID

(我正在打字,因为我没有您的数据来验证我的查询。可能有拼写错误)

答案 1 :(得分:1)

MySQL提供了一个聚合函数 GROUP_CONCAT

参考:https://dev.mysql.com/doc/refman/5.5/en/group-by-functions.html#function_group-concat

(我不打算在此重复整篇文档。)

一些重要说明:GROUP_CONCAT返回的字符串长度受max_group_concat_len变量设置以及max_allowed_packet变量的限制。

如果字符串较长,则会将其静默截断为允许的最大长度。您可能需要检查返回字符串的长度,并将其与允许的最大长度进行比较,以验证它是否未被截断。

DISTINCT关键字对于消除重复项非常有用。

ORDER BY对于使返回的字符串更具确定性非常有用。

默认分隔符是逗号,但也可以指定。

<强>后续

要通过type获取不同的电话号码列表,您可以在CASE表达式或IF函数中使用条件测试来有条件地返回值或NULL。只需将该表达式包装在像GROUP_CONCAT这样的聚合函数中,例如

SELECT GROUP_CONCAT( IF(t.type='main'  ,t.number,NULL) ) AS main_numbers
     , GROUP_CONCAT( IF(t.type='mobile',t.number,NULL) ) AS mobile_numbers
     , ...
  LEFT JOIN telephone t ON ...

如果您只需要每种类型一个号码,则可以使用MAX聚合函数,例如。

SELECT MAX( IF(t.type='main'  ,t.number,NULL) ) AS a_main_number
     , MAX( IF(t.type='mobile',t.number,NULL) ) AS a_mobile_number
     , ...
  LEFT JOIN telephone t ON ...

作为另一种选择(通常不是高性能),您可以使用SELECT列表中的相关子查询来获得等效结果。

SELECT c.customerID
     , ( SELECT t.number
           FROM customertelephone ct
           JOIN telephone t
             ON t.telephoneID = ct.telephoneID
            AND t.type = 'main'                -- main
          WHERE ct.customerID = c.customerID   -- match column from outer query
          ORDER BY t.telephoneID
          LIMIT 0,1                            -- first row
       ) AS main_number_1
     , ( SELECT t.number
           FROM customertelephone ct
           JOIN telephone t
             ON t.telephoneID = ct.telephoneID
            AND t.type = 'main'                -- main
          WHERE ct.customerID = c.customerID   -- match column from outer query
          ORDER BY t.telephoneID
          LIMIT 1,1                            -- second row
       ) AS main_number_2
     , ( SELECT t.number
           FROM customertelephone ct
           JOIN telephone t
             ON t.telephoneID = ct.telephoneID
            AND t.type = 'mobile'               -- mobile
          WHERE ct.customerID = c.customerID    -- match column from outer query
          ORDER BY t.telephoneID
          LIMIT 0,1
       ) AS mobile_number_1

 FROM customer c