将 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的主要内容,如果未能解决你的问题,请参考以下文章

将 T-SQL 转换为 MySQL

Microsoft T-SQL 到 Oracle SQL 的翻译

将 T-SQL 转换为 MySQL

T-SQL -- 将逗号分隔的列转换为多列

如何将 nvarchar 从 T-SQL 方言转换为 hiveQL?

需要帮助将 T-SQL 转换为 PostgreSQL