如何在 SQL 中使用条件逻辑来选择要加入的记录?
Posted
技术标签:
【中文标题】如何在 SQL 中使用条件逻辑来选择要加入的记录?【英文标题】:How can I use conditional logic to choose which record to join in SQL? 【发布时间】:2012-10-30 17:22:31 【问题描述】:我正在处理一个查询,除其他事项外,它会为给定 ID 号码的人选择一个电话号码。
这段代码搞定了:
LEFT JOIN [thedatabasename].dbo.[thetablename] Phone
ON p.PersonId = phone.PersonId
AND (phone.[Description] = 'Home Phone'
OR phone.[Description] = 'Cell Phone'
OR phone.[Description] is null)
这行得通,但我想要的是它做一些在概念上更像
IF EXISTS select [phone number] from <tablename> where description = 'Home phone'
ELSE IF EXISTS select [phone number] from <tablename> where description = 'cell phone'
ELSE IF EXISTS select [phone number] from <tablename> where description is null
因此它更喜欢“家庭电话”而不是“手机”,并且更喜欢“手机”而不是没有描述的电话。我不知道如何在我加入的过程中做到这一点。有没有办法做到这一点而不必编写三个单独的选择?此连接所属的查询丑陋且庞大,并且有一堆连接,因此将其分离出来会很困难。有什么建议吗?
【问题讨论】:
为什么不在选择中使用 CASE 语句?好像你有更多的控制权。您已经加入电话桌。供参考:techrepublic.com/article/… 只是为了澄清:当你说喜欢家庭电话时,如果你有 2 条记录,你不想输出手机记录:一个有手机和一个家庭电话,你的问题是 LEFT JOIN 输出两者。对吗? 对。如果他们有“家庭电话”,我只想得到那个,如果没有,那么我只想要他们的“手机”,如果没有,我想要一个带有 NULL 描述的电话号码。大多数人在电话号码表中都有多个条目,但类型各不相同。我认为我现在拥有的只是随机选择他们的家、手机或 NULL 电话号码。 【参考方案1】:由于三个电话号码位于不同的行中,因此您需要三个连接。使用case
语句选择第一个不为空的:
select
...
case
when phone1.[phone number] is not null then phone1.[phone number]
when phone2.[phone number] is not null then phone2.[phone number]
else phone3.[phone number]
end as [phone number]
...
LEFT JOIN [thedatabasename].dbo.[thetablename] phone1
ON p.PersonId = phone1.PersonId AND phone1.[Description] = 'Home Phone'
LEFT JOIN [thedatabasename].dbo.[thetablename] phone2
ON p.PersonId = phone2.PersonId AND phone2.[Description] = 'Cell Phone'
LEFT JOIN [thedatabasename].dbo.[thetablename] phone3
ON p.PersonId = phone3.PersonId AND phone3.[Description] is null
注意为连接的Phone
行提供的不同别名以区分它们。
如果您的数据库支持COALESCE
(或类似的),您可以将您的选择简化为:
select
...
coalesce (phone1.[phone number], phone2.[phone number], phone3.[phone number]) as [phone number]
...
【讨论】:
为什么不使用 COALESCE(phone1.[phone number], phone2.[phone number], phone3.[phone number]) 而不是 CASE? @sybkar 如果我自己编码,我会这样做,但问题并没有说明正在使用哪个数据库,也不是所有数据库都支持COALESCE
。我将添加它作为替代方案。干杯
抱歉,我认为这是 SQL 标准的一部分。我的错误:)
@sybkar 例如mysql用它的ifnull(a, b, c, ...)
函数实现了这个概念
@Bohemain,实际上,据我了解,MySQL 也使用 COALESCE,并使用 ifnull() 用于稍微不同的目的(MS-SQL isnull())【参考方案2】:
补充@Bohemian 的建议。如果您在多个查询中经常需要此功能,您可能需要编写一个表值函数(如果您的数据库有这样的东西)或查看来计算要显示的正确电话号码。那么所有对电话号码的引用将始终得到相同的号码。
另一种方法是使用标志字段(称为contactphone)将一个且唯一的电话号码标记为主要电话号码,并通过触发器保持更新。如果要联系的人可以指定他希望通过哪一个联系,这将很有用。
【讨论】:
【参考方案3】:另一个想法是为子查询中的每种电话类型分配一个排名,并只选择第一个。 这可以通过分析函数或 suquery (select count(*) etc) 来完成,这取决于您的服务器类型。 mysql 不支持分析函数,它可能看起来有点奇怪,但应该可以工作:-)。 使用 MSSQL 它提供:
select *
from (
select rank() over (
partition by Person.PersonId
order by
case when Phone.PhoneDescription = 'Home' then 1
when Phone.PhoneDescription = 'Cell' then 2
else 3 end ) as [rank],
Person.name, Phone.PhoneDescription, Phone.PhoneNumber
from Person
left join Phone on Person.PersonId = Phone.PersonId
) PhoneNumbers
where [rank] = 1
【讨论】:
【参考方案4】:假设您有权创建存储函数,我将用函数调用替换查询的那部分。决定返回哪个电话号码的逻辑可以写在这个函数中。这使主查询保持干净,并且该函数也可以在其他查询中重复使用。
MySQL 函数存根如下所示:
CREATE FUNCTION `getPhoneNumber`(_PersonId INT) RETURNS TEXT
BEGIN
// You SQL query here
END
【讨论】:
嗯,看起来确实很简单。或者,也许我可以创建一个视图。谢谢! 不是视图,因为视图不能返回值。您总是可以创建一个视图并将其与主 sql 查询连接起来,但这比仅使用函数调用更麻烦。除非您有充分的理由不这样做,否则我仍然会使用功能,并且在您的要求中,我找不到这样的理由。以上是关于如何在 SQL 中使用条件逻辑来选择要加入的记录?的主要内容,如果未能解决你的问题,请参考以下文章
SQL Partitioning,如何在分区组中选择满足一定条件的记录?