如何从包含日期时间列的表中获取某些行和“先前”行?
Posted
技术标签:
【中文标题】如何从包含日期时间列的表中获取某些行和“先前”行?【英文标题】:How can I get certain rows and the "previous" rows from a table that includes a datetime column? 【发布时间】:2014-07-01 18:03:35 【问题描述】:我有两张桌子,Alpha 和 Bravo。 Bravo 有一列 id
(整数,主键)和其他一些与此问题无关的列。 Alpha 有列id
(整数,主键),bravo_id
(Bravo 表的外键),special
(单个字符,对于大多数行为 null,但对于某些重要行具有值),created
(日期时间),以及其他一些与此问题无关的内容。
我想从 Alpha 中获取所有特殊行,加上对于每个特殊行我想从 Alpha 中获取与同一行 Beta 相关联的“上一个”非特殊行(也就是说,我想获得具有相同bravo_id
的Alpha 行和比特殊行的created
更早的最新created
),并且我需要保留特殊行及其上一行链接。
目前我正在使用 n+1 个查询来执行此操作:
SELECT id, bravo_id, created FROM Alpha WHERE special IS NOT NULL
对初始查询中的每个结果进行类似这样的查询:
SELECT id, created FROM Alpha
WHERE special IS NULL AND bravo_id = BrvN AND created < CrtN ORDER BY created DESC
显然这是非常低效的。有没有一种方法可以在单个查询中检索此信息,将每个特殊行及其前一个非特殊行放在结果的单行中?
我们的产品同时支持 SQL Server(如果相关,则为 2008 R2)和 Oracle(如果相关,则为 11g),因此适用于这两者的查询将是理想的,但只查询两者中的一个就可以了。
编辑:“创建”可能是用词不当。该列中的日期时间是创建引用对象的时间,而 不是 是它被输入数据库的时间(可能是几秒到几年后的任何时间)。基于 created
列的 Alpha 行排序与基于 id
列的排序(这是传统的递增标识/序列)几乎没有相关性。
【问题讨论】:
在 Oracle(或 SQL Server 2012)中,您可以使用lag()
函数从“上一个”行中获取值。
【参考方案1】:
SELECT a.Id, a.Bravo_Id, a.Created, d.Id, d.Created FROM @Alpha a
OUTER APPLY
(
SELECT TOP 1 da.id, da.Created
FROM @Alpha da
WHERE da.Special IS NULL
AND da.Bravo_Id = a.Bravo_Id
AND da.Created < a.Created
ORDER BY da.Created DESC
) d
WHERE a.Special IS NOT NULL
您可以使用 apply (ms sql server query) 绑定这两个查询
【讨论】:
【参考方案2】:这适用于 SQL Server 和 Oracle:
select A.id, A.bravo_id, A.created, B.id, B.created
from Alpha A
left join Alpha B on A.bravo_id = B.bravo_id
and B.created < A.created
and B.special is null
where A.special is not null
and (B.created is null or
B.created = (select max(S.created)
from Alpha S
where S.special is null
and S.bravo_id = A.bravo_id
and S.created < A.created))
它在所有行中使用相同的外键并创建了一个较低/较旧的连接,然后使用 where 子句将它们过滤掉(注意不要排除没有较旧行的 A 行)。
【讨论】:
【参考方案3】:很遗憾,SQL Server 2008 不支持累积和。这是解决问题的方法。
对于 Alpha 中的每一行,计算 在 alpha 之后的特殊行数。这将分配一个分组。在组内,然后使用row_number()
枚举值,并选择前两个。
select a.*
from (select a.*,
row_number() over (partition by bravo, grp order by id desc) as seqnum
from (select a.*,
(select count(*)
from alpha a2
where a2.bravo = a.bravo and a2.special = 1 and
a2.id >= a.id
) as grp
from alpha a
) a
) a
where seqnum <= 2;
在 Oracle(或 SQL Server 2012)中,您可以这样写:
select a.*
from (select a.*,
row_number() over (partition by bravo, grp order by id desc) as seqnum
from (select a.*,
sum(case when special = 1 then 1 else 0 end) over (partition by bravo order by id desc
) as grp
from alpha a
) a
) a
where seqnum <= 2;
【讨论】:
这似乎有潜力,但我意识到我没有正确解释created
列的使用。请查看我对原始问题的编辑,并检查您的答案是否需要调整。以上是关于如何从包含日期时间列的表中获取某些行和“先前”行?的主要内容,如果未能解决你的问题,请参考以下文章