如何在 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查询效率where语句条件的先后次序应如何写

要提高SQL查询效率where语句条件的先后次序应如何写

SQL Partitioning,如何在分区组中选择满足一定条件的记录?

solr:如何加入三个核心?

在sql server 数据库字段中如何加入回车符和换行符?

如何比较 SQL where 条件中的两个逻辑表达式? [关闭]