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 从多个表中选择列而不重复数据的主要内容,如果未能解决你的问题,请参考以下文章