如何在连接三个表时检索值或null而不重复?

时间:2012-07-06 20:47:42

标签: sql sql-server tsql

从以下三个表中,我需要:

  1. 为People中的每条记录返回一行,如果他们有多个关联联系人,请不要复制该人。
  2. 包含关联的ContactValue,其中ContactType为HOMEPHONE,日期范围的开头在此之前,日期范围的结尾比现在晚或为null。如果某人没有家庭电话,仍会显示此人,但会显示NULL作为ContactValue的值。
  3. PersonId(PK,int)
    FullName(nvarchar)

    PeopleContacts

    PersonId(PK,int)
    ContactId(PK,int)
    StartValidDate(PK,datetime)
    EndValidDate(datetime,null)

    联系人

    ContactId(PK,int)
    ContactType(nvarchar)
    ContactValue(nvarchar)

    People 表包含一个唯一的人员列表。 PeopleContacts 可能包含针对不同联系人类型的每个人的多个联系人关联,以及联系人适合的日期范围。 通讯录包含某些类型的联系人值列表。例如,“WORKPHONE”的ContactType和“(555)555 5555”的ContactValue。

    要求:

    1. 为People中的每条记录返回一行,如果他们有多个关联联系人,请不要复制该人。
    2. 包含关联的ContactValue,其中ContactType为HOMEPHONE,日期范围的开头在此之前,日期范围的结尾比现在晚或为null。如果某人没有家庭电话,仍会显示此人,但会显示NULL作为ContactValue的值。
    3. 如果人员包含三行,请执行以下操作:

      1     Jones, Bob
      2     Smith, Bob
      3     Smith, Fred
      

      PeopleContacts包含四行,其中包含:

      1     4    01/01/2012     NULL
      2     1    01/01/2012     02/01/2012
      2     2    02/02/2012     NULL
      3     3    01/01/2012     NULL
      3     4    01/01/2012     NULL
      

      和“联系人”包含四行,其中包含:

      1     HOMEPHONE     (555) 555 5252
      2     HOMEPHONE     (666) 666 6666
      3     HOMEPHONE     (777) 777 7777
      4     WORKPHONE     (555) 555 5555
      

      正确查询的输出(如果在02/02/2012之后运行)应如下所示:

      FullName     ContactValue
      --------     ------------
      Jones, Bob   NULL
      Smith, Bob   (666) 666 6666
      Smith, Fred  (777) 777 7777
      

      Jones,Bob与联系人有关联,但它是一个WORKPHONE,因此ContactValue应为NULL。史密斯,鲍勃与两个HOMEPHONE记录有关联,但只有一个,(666)666 6666,仍然有一个有效的日期范围。 Smith,Fred与HOMEPHONE和WORKPHONE都有联系。

4 个答案:

答案 0 :(得分:1)

我会使用公用表表达式来解决这个问题 - 这应该比使用APPLY更有效,它将针对每一行进行评估。使用CTE,您只需获得一个结果集,然后对其进行左外连接。

我会使用RANK确保您只收到一个联系人...通过订购CTE并按第一级加入CTE,您只需获得一次联系。

SQL看起来像这样:

WITH [CTE] AS (
SELECT
        pc.PersonId
    ,   pc.ContactValue
    ,   RANK() OVER (PARTITION BY PersonId ORDER BY ContactValue DESC) AS [Seed]
FROM
    PeopleContacts pc
    INNER JOIN  Contacts c ON (pc.ContactId = c.ContactId AND c.ContactType='HOME')
    WHERE pc.StartValidDate <= GETDATE()
    AND (pc.EndValidDate > GETDATE() OR pc.EndValidDate IS NULL)
)

SELECT      FullName
        ,   ISNULL(CTE.ContactValue, '') 
FROM People p
LEFT OUTER JOIN CTE ON (CTE.PersonId = p.PersonID AND CTE.Seed = 1)

(此代码未经测试,如果您设置了sql fiddle,那么我很乐意让它正常运行)

答案 1 :(得分:0)

我相信以下查询可以满足我的需求,但我喜欢反馈或更好的方法来实现这一目标:

SELECT FullName, c.ContactValue FROM People p
OUTER APPLY
    (
    SELECT TOP 1 ContactValue FROM PeopleContacts pc 
    LEFT OUTER JOIN Contacts c ON pc.ContactId = c.ContactId AND c.ContactType='HOME'
    WHERE pc.PersonId = p.PersonId
    AND pc.StartValidDate <= GETDATE()
    AND (pc.EndValidDate > GETDATE() OR pc.EndValidDate IS NULL)
    ORDER BY ContactValue DESC) c

答案 2 :(得分:0)

以下代码似乎正确无误:

declare @tPeoples table (
    id int,
    name nvarchar(50)
)

declare @tContacts table (
    id int,
    type char(10),
    value nvarchar(50)
)

declare @tPeopelContact table (
    peopleId int,
    contactId int,
    starting datetime2,
    ending datetime2
)

insert into @tPeoples (id, name) values
(1, 'Jones, Bob'),
(2, 'Smith, Bob'),
(3, 'Smith, Fred')

insert into @tContacts (id, type, value ) values
(1,     'HOMEPHONE',  '(555) 555 5252'),
(2,     'HOMEPHONE',  '(666) 666 6666'),
(3,     'HOMEPHONE',  '(777) 777 7777'),
(4,     'WORKPHONE',  '(555) 555 5555'),
(5,     'HOMEPHONE',  '(000) 123 5252')

insert into @tPeopelContact (peopleId, contactId, starting, ending) values
(1,     4,    '20120101',    NULL),
(2,     1,    '20120101',   '20120201'),
(2,     2,    '20120201',     NULL),
(3,     3,    '20120101',     NULL),
(3,     4,    '20120101',     NULL)


declare @currentdate datetime
declare @type char(10)

set @currentdate = '20120202'
set @type = 'HOMEPHONE'

select
    p.name, vc.value
from
    @tPeoples p
    left join 
        (select
            ROW_NUMBER () over (partition by peopleId order by starting) as rn, *
        from
            @tPeopelContact pc
            join @tContacts c on pc.contactId = c.id
        where 
            type = @type and
            starting < @currentdate and ( ending is null or ending >= @currentdate) 
        ) vc on rn = 1 and vc.peopleId = p.id

答案 3 :(得分:0)

以下查询似乎正确无误:

SELECT A.FullName, B.ContactValue
FROM People A
LEFT JOIN ( SELECT  X.PersonId,
                    Y.ContactValue,
                    ROW_NUMBER() OVER (PARTITION BY X.PersonId ORDER BY ContactId ) AS RowNumber
            FROM PeopleContacts X
            LEFT JOIN Contacts Y ON Y.ContactId = X.ContactId 
        ) B ON B.PersonId = A.PersonId AND B.RowNumber = 1
相关问题