0

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

  1. 为 People 中的每条记录返回一行,如果他们有多个关联联系人,则不要复制该人。
  2. 包括关联的 ContactValue,其中 ContactType 为 HOMEPHONE,日期范围的开始时间早于现在,日期范围的结束时间晚于现在或为空。如果一个人没有家庭电话,仍然显示该人,但显示 NULL 作为 ContactValue 的值。

人们

PersonId (PK, int)
全名 (nvarchar)

人员联系方式

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 作为 ContactValue 的值。

如果 People 包含三行,其中:

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

正确查询的输出(如果在 2012 年 2 月 2 日之后运行)应如下所示:

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

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

4

4 回答 4

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,那么我很乐意让它工作)

于 2012-07-06T21:48:13.207 回答
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
于 2012-07-06T20:50:47.440 回答
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
于 2012-07-06T21:39:24.140 回答
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
于 2012-07-07T07:38:52.433 回答