我的数据如下:
我需要做的是,对于具有相同ClientId
的记录,我需要对PlaceId
不为空的连续行(使用CpId)进行分组,并找到每个行的第一行和最后一行group,以便我可以从第一行检索DateAdmitted
值,从最后一行检索DateDischarged
值。因此,上述数据需要像这样组织,然后根据我需要的值进行过滤:
使用上面的示例,我希望基于ClientId
:
ClientId FirstCpIdInSet DateAdmitted LastCpIdInSet DateDischarged
-----------------------------------------------------------------------------
1967 NULL NULL NULL NULL
1983 45 1986-12-29 45 1987-10-09
1983 47 1990-10-01 49 2009-04-12
1983 52 2009-08-31 52 2009-11-30
1988 62 1997-12-15 65 2000-01-07
ClientId
1967可以从结果集中排除,因为它永远不会有PlaceId
不为空的行。还有其他几点需要注意:
CpId
作为IDENTITY
创建的临时表中获取的,并且该表填充了严格的ORDER BY
,因此CpId
是顺序的需要的订单。PlaceId
且单个ClientId
是连续的行,DateAdmitted
应该等于上一行中的DateDischarged
。如果可能的话,我真的希望能够在没有光标的情况下做到这一点,但是在困惑了两天之后我就无法理解。这是在SQL Server 2008 R2上。
答案 0 :(得分:2)
确实令人讨厌的查询。像大多数SQL问题一样,它以正确的顺序解决问题的不同方面。我的解决方案不使用游标。它确实使用外部应用和分区。
实现: row_number()over(xx order by yy)本身不起作用,因为yy通常跨越多个xx分区。
示例数据:
id state
1 a
2 a
3 b
4 c
期望的范围:
1 <= x < 3
3 <= x < 4
4 <= x
步骤1 - 使用外部应用查找每行的下一个状态转换。这允许您根据所需的任何条件检查每一行的下一个值。此步骤可能会生成比您想要的更多信息。几行可以转换为相同的值。在此示例中,id 1和2在id 3处转换。
伪代码:
select t1.id, t1.state, t3.id, t3.state
from table1 t1
outer apply
(
select
--only grab one row
top 1 t2.id, t2.state
from table1 t2
where
--grab a value that's generated after the current value.
t1.id < t2.id
-- add whatever join logic you need for your case.
and t1.memberid=t2.memberid
-- make sure you get the correct order, typically an identity or time
order by t2.id asc
) T3
此查询生成如下内容:
id state id state
1 a 3 b
2 a 3 b
3 b 4 c
4 c null null
我们不希望id = 2的行。
步骤2 - 通过转换列进行分区可以获得行号,当发生状态转换时,该值始终为1。只需按1过滤,即可进行状态转换。
初步结果:
row_number id state id state
1 1 a 3 b
2 2 a 3 b
1 3 b 4 c
1 4 c null null
过滤结果:
row_number id state id state
1 1 a 3 b
1 3 b 4 c
1 4 c null null
答案 1 :(得分:1)
你没有说出你的第一个基础和最后一个基础。我假设它是CPID。您可以使用排名功能执行此操作:
select ClientID, PlaceId,
max(CpID) as max(CPId),
min(case when seqnumasc = 1 then DateAdmitted end) as DateAdmitted,
max(case when seqnumdesc = 1 then DateDischarged end) as DateDischarged
from (select t.*,
row_number() over (partition by clientID, placeID order by cpid) as seqnumasc
row_number() over (partition by clientID, placeID order by cpid desc) as seqnumdesc
from t
) t
where placeID is not null
group by ClientID, placeID
这将按顺序放置nubmers以确定每个组中的第一行和最后一行。但是,为什么你不能只使用最小和最大日期添加和释放?
基于增强的信息。 。 。
现在问题似乎是根据以下条件定义记录的“集合”:
如果是这样,以下内容将为您提供“设置ID”。这基于从CPID中减去序列号,使用技巧将连续值汇集在一起。此差异是连续值的常量,提供设置ID。
select clientid, setid,
min(DateAdmitted) as DateAdmitted,
max(DateDischarged) as DateDischarged,
min(cpid) as minCPID,
max(cpid) as maxCPID
from (select clientid, setid, cpid,
row_number() over (partition by clientid, setid order by cpid) as seqnum,
count(*) over (partition by clientid, setid) as setsize
from (select t.*,
(cpid - row_number() over (partition by clientid order by cpid)
) as setid
from t
where PlaceID is not NULL
) t
) t
group by clientid, setid