使用没有重复的父表连接2个子表

时间:2012-11-23 16:18:35

标签: php mysql sorting join

问题

我有3张桌子:人,电话和电子邮件。每个人都有一个独特的ID,每个人可以有多个号码或多个电子邮件。

简化它看起来像这样:

    +---------+----------+
    | ID      | Name     |
    +---------+----------+
    | 5000003 | Amy      |
    | 5000004 | George   |
    | 5000005 | John     |
    | 5000008 | Steven   |
    | 8000009 | Ashley   |
    +---------+----------+

    +---------+-----------------+
    | ID      | Number          |
    +---------+-----------------+
    | 5000005 | 5551234         |
    | 5000005 | 5154324         |
    | 5000008 | 2487312         |
    | 8000009 | 7134584         |
    | 5000008 | 8451384         |
    +---------+-----------------+

+---------+------------------------------+
| ID      | Email                        |
+---------+------------------------------+
| 5000005 | Smithley@goodmail.com.com    |
| 5000005 | Smithley.j@gmail.com         |
| 5000008 | Smithley@gmail.com           |
| 5000008 | tech@goodmail.com            |
| 5000008 | feler@campus.uni.com         |
| 8000009 | Ashley.hill86@gmail.com      |
| 5000004 | georgestanko@hotmail.com     |
+---------+------------------------------+

我正在尝试将它们连接在一起而不重复。当我尝试只与人群发送电子邮件或仅与人们合作电话时,它的效果很好。

SELECT People.Name, People.ID, Phones.Number
    FROM People 
    LEFT OUTER JOIN Phones ON People.ID=Phones.ID 
    ORDER BY Name, ID, Number;
+----------+---------+-----------------+
| Name     | ID      | Number          |
+----------+---------+-----------------+
| Steven   | 5000008 | 8451384         |
| Steven   | 5000008 | 24887312        |
| John     | 5000005 | 5551234         |
| John     | 5000005 | 5154324         |
| George   | 5000004 | NULL            |
| Ashley   | 8000009 | 7134584         |
| Amy      | 5000003 | NULL            |
+----------+---------+-----------------+

SELECT People.Name, People.ID, Emails.Email
    FROM People 
    LEFT OUTER JOIN Emails ON People.ID=Emails.ID 
    ORDER BY Name, ID, Email;
+----------+---------+------------------------------+
| Name     | ID      | Email                        |
+----------+---------+------------------------------+
| Steven   | 5000008 | Smithley@gmail.com           |
| Steven   | 5000008 | tech@goodmail.com            |
| Steven   | 5000008 | feler@campus.uni.com         |
| John     | 5000005 | Smithley@goodmail.com.com    |
| John     | 5000005 | Smithley.j@gmail.com         |
| George   | 5000004 | georgestanko@hotmail.com     |
| Ashley   | 8000009 | Ashley.hill86@gmail.com      |
| Amy      | 5000003 | NULL                         |
+----------+---------+------------------------------+

然而,当我尝试加入人们的电子邮件和电话时 - 我明白了:

SELECT People.Name, People.ID, Phones.Number, Emails.Email
    FROM People 
    LEFT OUTER JOIN Phones ON People.ID = Phones.ID
    LEFT OUTER JOIN Emails ON People.ID = Emails.ID 
    ORDER BY Name, ID, Number, Email;

+----------+---------+-----------------+------------------------------+
| Name     | ID      | Number          | Email                        |
+----------+---------+-----------------+------------------------------+
| Steven   | 5000008 | 8451384         | feler@campus.uni.com         |
| Steven   | 5000008 | 8451384         | Smithley@gmail.com           |
| Steven   | 5000008 | 8451384         | tech@goodmail.com            |
| Steven   | 5000008 | 24887312        | feler@campus.uni.com         |
| Steven   | 5000008 | 24887312        | Smithley@gmail.com           |
| Steven   | 5000008 | 24887312        | tech@goodmail.com            |
| John     | 5000005 | 5551234         | Smithley@goodmail.com        |
| John     | 5000005 | 5551234         | Smithley.j@gmail.com         |
| John     | 5000005 | 5154324         | Smithley@goodmail.com        |
| John     | 5000005 | 5154324         | Smithley.j@gmail.com         |
| George   | 5000004 | NULL            | georgestanko@hotmail.com     |
| Ashley   | 8000009 | 7134584         | Ashley.hill86@gmail.com      |
| Amy      | 5000003 | NULL            | NULL                         |
+----------+---------+-----------------+------------------------------+

会发生什么 - 如果一个人有2个号码,他的所有电子邮件都会被显示两次(它们无法排序!这意味着它们不能被@last删除)

我想要的是什么:

底线,和@last一起玩,我想最终得到像这样的东西,但是如果我没有以正确的方式排列ORDER列,那么@last将无效 - 这似乎是个大问题。 .Orderin电子邮件专栏。因为从上面的例子看:

史蒂文有2个电话号码和3封电子邮件。每个电子邮件都会出现带有号码的JOIN电子邮件 - 因此无法排序的重复值(SORT BY对它们不起作用)。

**THIS IS WHAT I WANT**
+----------+---------+-----------------+------------------------------+
| Name     | ID      | Number          | Email                        |
+----------+---------+-----------------+------------------------------+
| Steven   | 5000008 | 8451384         | feler@campus.uni.com         |
|          |         | 24887312        | Smithley@gmail.com           |
|          |         |                 | tech@goodmail.com            |
| John     | 5000005 | 5551234         | Smithley@goodmail.com        |
|          |         | 5154324         | Smithley.j@gmail.com         |
| George   | 5000004 | NULL            | georgestanko@hotmail.com     |
| Ashley   | 8000009 | 7134584         | Ashley.hill86@gmail.com      |
| Amy      | 5000003 | NULL            | NULL                         |
+----------+---------+-----------------+------------------------------+

现在我被告知最好将电子邮件和号码保存在单独的表中,因为可以有很多电子邮件。因此,如果它是如此常见的事情,那么什么不是一个简单的解决方案?

我对PHP解决方案感到满意。

我现在知道怎么做才能满足它,但不是那么漂亮。

如果我使用GROUP_CONTACT这样做,我会得到满意的结果,但它看起来并不漂亮:我不能在它旁边加上“电子邮件类型=工作”。

   SELECT People.Ime,  
    GROUP_CONCAT(DISTINCT Phones.Number),  
    GROUP_CONCAT(DISTINCT Emails.Email)  
    FROM People 
    LEFT OUTER JOIN Phones ON People.ID=Phones.ID
    LEFT OUTER JOIN Emails ON People.ID=Emails.ID
    GROUP BY Name;
+----------+----------------------------------------------+---------------------------------------------------------------------+
| Name     | GROUP_CONCAT(DISTINCT Phones.Number)         | GROUP_CONCAT(DISTINCT Emails.Email)                                 |
+----------+----------------------------------------------+---------------------------------------------------------------------+
| Steven   | 8451384,24887312                             | Smithley@gmail.com,tech@goodmail.com,feler@campus.uni.com           |
| John     | 5551234,5154324                              | Smithley@goodmail.com,Smithley.j@gmail.com                          |
| George   | NULL                                         | georgestanko@hotmail.com                                            |
| Ashley   | 7134584                                      | Ashley.hill86@gmail.com                                             |
| Amy      | NULL                                         | NULL                                                                |
+----------+----------------------------------------------+---------------------------------------------------------------------+

3 个答案:

答案 0 :(得分:1)

你想要的实际上并不是你想要的东西,如果这有任何意义......你无法以编程方式对数据库输出做任何事情,你需要用它做一些事情(除非你只是直接在您的数据库上运行查询。)

既然你说过“我对PHP解决方案感到满意。” ...你真正想要的东西就像一个PHP“用户”对象,类似的东西(当然这都是假设的):

<?php

class User {

  private $_id;
  private $_telNos = array();
  private $_emails = array();

  public function __construct($iUserId = null, $oDatabaseAbstractionObject = null) {
    if(!is_null($iUserId)) $this->setId($iUserId);
    if(!is_null($oDatabaseConnectionObject)) $this->load($iUserId, $oDatabaseAbstractionObject);
  }

  public setId($iUserId) {
    $this->_id = (int) $iUserId;
  }

  public getId() {
    return $this->_id;
  }

  /* telephone and email setters and getters */

  public function load($iUserId, $oDatabaseAbstractionObject) {
    /* error trapping - for example if $iUserId is null */

    $this->setTelNos($oDatabaseAbstractionObject->readTelNos($iUserId));
    $this->setEmails($oDatabaseAbstractionObject->readEmails(iUserId));
  }
}

?>

您的数据库抽象对象只需要执行一些非常简单的查询来读取您的用户,用户电子邮件和电话号码表,并将结果作为数组返回,然后您可以将这些数据直接放入PHP对象中。例如:

<?php

/**
 * this implements a database connection object as a private class member
 */
class DBUser {

  private $_conn;

  /* constructor other functionality */

  /**
   * method to pass an SQL query to the database and return an array of results
   */
  public function readTelNos($iUserId) {
    return $this->_conn->read("SELECT `number` from `tel` WHERE `user_id` = " . (int) $iUserId);
  }

}
?>

这会将您的问题分解为更小,更容易处理的问题,并将它们全部包装在您可以实际使用的漂亮PHP对象中。

如果电话号码和电子邮件可以使用$oUser->getEmails();之类的内容轻松检索,并且如果它们作为关联数组存储在对象中,您甚至可以通过“标签”检索它们,那么您的用户对象将会有一个列表$oUser->getEmail('work');

答案 1 :(得分:1)

如果我理解正确,您还需要将其他字段与电子邮件相关联(例如:输入)。

使用group_concat,您可以将电子邮件类型添加到结果中,在我的示例“;”中将邮件类型分开和“,”将结果分开。

SELECT u.name,  
    GROUP_CONCAT( distinct p.phone),  
    GROUP_CONCAT( distinct 'mail=',e.mail,';type=' ,type)  
    FROM people u
    LEFT OUTER JOIN phone p ON u.id=p.id
    LEFT OUTER JOIN mail e ON u.id=e.id
    GROUP BY u.id

答案 2 :(得分:0)

Fist创建临时表名称temp_tb而不是插入记录。然后使用Row_Number()进行分区,并删除多次列出的所有记录。就像打击一样。

    Insert into temp_tb SELECT People.Name, People.ID, Phones.Number, Emails.Email
    FROM People 
    LEFT OUTER JOIN Phones ON People.ID = Phones.ID
   LEFT OUTER JOIN Emails ON People.ID = Emails.ID 
   ORDER BY Name, ID, Number, Email;
   SELECT * from temp_tb 
    With A as
  (
 select People.Name, People.ID, Phones.Number, Emails.Email ROW_NUMBER() 
  OVER (Partition by ID ORDER BY Fee_temp.Name) As Number from temp_tb 
 )
 DELETE FROM A WHERE Number>1
 SELECT * FROM temp_tb 

希望它对你有所帮助。