SQL 从多个表中选择列而不重复数据

Posted

技术标签:

【中文标题】SQL 从多个表中选择列而不重复数据【英文标题】:SQL SELECT Columns From Multiple Tables Without Repeating Data 【发布时间】:2011-11-30 03:38:15 【问题描述】:

我正在尝试使用以下 sql 语句查询 2 个表,以尝试从每个包含特定 id 的表中返回所有记录。

SELECT Phone.Phone, Email.Email FROM Contacts.Phone, Contacts.Email 
WHERE Phone.ContactId = :contactId AND Email.ContactId = :contactId

Contacts.Phone 表包含给定 ID 的 2 个电话号码,Contacts.Email 包含给定 ID 的 1 个电子邮件。使用上面的 sql 查询,我得到以下行返回。当然,这只是我的每个表的结果集在行数上匹配的情况的一个示例。

Row 1: 555-555-5555 - email@email.com
Row 2: 666-666-6666 - email@email.com

当我尝试获取时,重复电子邮件以便填写第二行:

Row 1: 555-555-5555 - email@email.com
Row 2: 666-666-6666 - NULL

我想我需要使用UNION 以某种方式加入表,但我无法确切地弄清楚如何编写 sql 语句。另一种选择是执行 2 个单独的 SQL 查询,这会更容易,但我认为在性能方面最好在一个查询中收集我需要的所有数据。

我正在使用 mysql

【问题讨论】:

我认为您需要LEFT JOIN 电子邮件表。 @ryandlf 一个联系人 ID 有 2 个(或更多)电话号码,一个联系人 ID 可以有 2 个(或更多)电子邮件? 我认为您想使用两个子查询,每个子查询都有一些 ROWNUM/rank/row_number 的变体,并在该字段上执行完全外连接。对于特定查询,您必须告诉我们您使用的是什么 RDBMS(MySQL?Oracle?SQL Server?PostgreSQL?其他?)。也就是说,我认为您的说法是“在性能方面,最好在一个查询中收集我需要的所有数据”是错误的。一个提取 n 行的查询优于 n 个每个提取一行的查询,但没有理由将两个逻辑上分离的查询合并为一个。 是的,我正在建立一个联系人数据库,我的想法是动态电话号码、电子邮件等,因此对于我添加到数据库中的每个联系人,我可以拥有任意数量的电话号码(家庭、工作、手机等)以及电子邮件、地址等。这就是为什么我将它们分成自己的表并使用外部 id 将它们与初始联系人表连接起来。 【参考方案1】:

使用left outer join 来解决您的问题,如果您使用的是 MS SQL,查询将看起来像这样:

SELECT 
    Phone.Phone, 
    Email.Email 
FROM 
    Contacts.Phone 

Left Outer Join Contacts.Email ON Phone.ContactId = Email.ContactId

您的结果将如下所示:

Row 1: 555-555-5555 - email@email.com 
Row 2: 666-666-6666 - NULL 

【讨论】:

【参考方案2】:

首先,我认为是这样的:

另一种选择是执行 2 个单独的 SQL 查询,这会更容易,但我认为在性能方面最好在一个查询中收集我需要的所有数据。

有点误导。一个复杂查询和两个简单​​查询之间的性能差异将非常小。也就是说,您建议使用UNION;如果您确实想使用单个查询,可以这样做:

SELECT 'EMAIL', Email.Email
  FROM Contacts.Email
 WHERE Email.ContactId = :contactId
UNION ALL
SELECT 'PHONE', Phone.Phone
  FROM Contacts.Phone
 WHERE Phone.ContactId = :contactId
 ORDER BY 1 -- put e-mail addresses before phone-numbers
;

第一列将返回结果的“类型”,第二列将包含数据。

【讨论】:

谢谢。对我来说,这种方法实际上比返回空值好一点,因为我可以检查第一列并相应地放置数据。就我所知,order by 只是说 Order by table 1,对吗?首先返回电子邮件数据是否有特定原因? @ryandlf:不客气。 ORDER BY 1 实际上表示“按第一个字段排序”(在本例中为“类型”字段)。将电子邮件放在首位的唯一原因是,我认为先获取所有电子邮件,然后再获取所有其他电子邮件会更好,并且'EMAIL' 排在'PHONE' 之前。如果您首先需要电话号码,您可以使用ORDER BY 1 DESC。如果你根本不关心订单——如果你没事的话。可能会获得(比如说)一些电子邮件地址,然后是一些电话号码,然后是更多的电子邮件地址——然后你可以完全放弃ORDER BY 1【参考方案3】:

如果您真的希望两列中的结果包含重复数据的 NULL,那么如果 MySQL 支持一些其他 DBMS 系统允许的 RANK() OVER (PARTITION BY ... 语法,那将真的很有帮助。遗憾的是它没有,但 Daniel Vassallo 通过在this question 中描述如何在 MySQL 中完成它来拯救它。

根据你的情况调整他的方法:

SELECT Phone.Phone,
    CASE Email.Email
    WHEN @curEmail THEN NULL
    ELSE @curEmail := Email.Email END AS Email
FROM Contacts.Phone, Contacts.Email, (SELECT @curEmail := '') AS r
WHERE Phone.ContactId = :contactId AND Email.ContactId = :contactId

当我在您问题中的测试数据上运行此程序时,我得到:

Row 1: 555-555-5555 - email@email.com
Row 2: 666-666-6666 - NULL

这是想要的结果。

【讨论】:

以上是关于SQL 从多个表中选择列而不重复数据的主要内容,如果未能解决你的问题,请参考以下文章

重命名mysql表中的列而不必重复其类型定义

将数据从 Excel 文件导入 SQL 表而不重复?

SQL 服务器。从2个表中选择数据[重复]

选择多个列而不进行多个查询 php mysql

如何在现有表中添加额外的列而不丢失数据

sql中如何使一列中的多个重复数据只显示第一条