从以下三个表中,我需要:
PersonId(PK,int)
FullName(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 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都有联系。
答案 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