将 T-SQL 交叉应用转换为 Oracle
Posted
技术标签:
【中文标题】将 T-SQL 交叉应用转换为 Oracle【英文标题】:Convert T-SQL Cross Apply to Oracle 【发布时间】:2015-04-25 06:19:46 【问题描述】:我希望将此使用交叉应用的 SQL Server (T-SQL) 查询转换为 Oracle 11g。 Oracle 直到 12g 才支持 Cross Apply,所以我必须找到解决方法。查询背后的想法是对于 = 'Foobar' 的每个 Tab.Name,我需要找到具有相同 ID 的前一行的名称,该 ID 由 Tab.Date 排序。 (此表包含 1 个具有不同名称和日期的 ID 的多行)。
这是 T-SQL 代码:
SELECT DISTINCT t1.ID
t1.Name,
t1.Date,
t2.Date as 'PreviousDate',
t2.Name as 'PreviousName'
FROM Tab t1
OUTER apply (SELECT TOP 1 t2.Date,
t2.Name
FROM Tab t2
WHERE t1.Id = t2.Id
ORDER BY t2.Date DESC) t2
WHERE t1.Name = 'Foobar' )
从技术上讲,我能够使用 LEFT JOIN 和 LAG() 函数在 Oracle 中重新创建相同的功能:
SELECT DISTINCT t1.ID
t1.Name,
t1.Date,
t2.PreviousDate as PreviousDate,
t2.PreviousName as PreviousName
FROM Tab t1
LEFT JOIN (
SELECT ID,
LAG(Name) OVER (PARTITION BY ID ORDER BY PreviousDate) as PreviousName,
LAG(Date) OVER (PARTITION BY ID ORDER BY PreviousDate) as PreviousDate
FROM Tab) t2 ON t2.ID = t1.ID
WHERE t1.Name = 'Foobar'
问题在于它执行 Oracle 查询的顺序。它将从 Tab 中拉回所有行,对它们进行排序(因为 LAG 函数),然后在将其连接到主查询时使用 ON 语句将它们过滤掉。该表有数百万条记录,因此对每个 ID 这样做是不可行的。基本上,我想更改子查询中的操作顺序,只为单个 ID 拉回行,对这些行进行排序以找到前一个,然后加入。关于如何调整它的任何想法?
TL;DR SQL Server: 过滤器、订单、连接 Oracle:订单、过滤器、连接
【问题讨论】:
你的内部查询是t1.Date = t2.Date
,然后是order by t2.Date
。所以本质上它是在选择一个随机的行。
@Andomar 这听起来很有趣,你能详细说明为什么会这样吗?
@Wjdavis5: 好吧,如果你首先要求所有日期都等于某个值(比如t1.Date
),然后按该值排序,那么排序不会有太大作用。
@Andomar 感谢您指出这一点,它不应该在 t1.Date = t2.Date 上进行过滤。我现在已经更新了查询。
MS SQL Server 的 APPLY
在 Oracle 中变为 LATERAL
。但是,我不确定引入了什么修订版。我相信,在最新版本中,Oracle 也确实支持APPLY
。
【参考方案1】:
您可以使用row_number()
查找每个(id)
组的最新行:
select *
from tab t1
left join
(
select row_number() over (
partition by id
order by Date desc) as rn
, *
from t2
) t2
on t1.id = t2.id
and t2.rn = 1 -- Latest row per id
【讨论】:
不太对。我正在寻找 Name = 'Foobar' 的前一行(按日期排序)。名称可以包含任何日期,因此不一定是 ID 的最早行。以上是关于将 T-SQL 交叉应用转换为 Oracle的主要内容,如果未能解决你的问题,请参考以下文章
Microsoft T-SQL 到 Oracle SQL 的翻译