繁体   English   中英

等效于Oracle中SQL Server的“ TOP 1”(不使用rownum或row_number())

[英]Equivalent of SQL Server's “TOP 1” in Oracle (without using rownum or row_number())

我有一个CONTRACTINFO表,该表存储合同的折扣代码(该代码可以不时更改,具体取决于合同的类型或期限)。

CREATE TABLE CONTRACTINFO
(
    ID CHAR(8),
    BASERECORD CHAR(1),
    DATE CHAR(8),
    DISCOUNTCODE CHAR(1)
)

每个月,我们需要根据付款paymentdatediscountcode计算客户必须支付的费用。

CREATE TABLE PAYMENT
(
    CONTRACTID CHAR(8),
    TIME NUMBER(12),
    PAYMENTDATE CHAR(8)
)

折扣代码是通过从CONTRACTINFO表中获取date < paymentdate的最后一条记录来确定的。

我创建了一个简单的示例来显示所需的结果(以黄色显示)。

在此处输入图片说明

在SQL Server中,我可以使用下面的相关子查询轻松实现此目的:

SELECT 
    PA.*,
    (SELECT TOP 1 DISCOUNTCODE 
     FROM CONTRACTINFO 
     WHERE ID = PA.CONTRACTID 
       AND DATE < PA.PAYMENTDATE 
     ORDER BY DATE DESC) AS DISCOUNTCODE 
FROM 
    PAYMENT PA
INNER JOIN 
    CONTRACTINFO CI ON PA.CONTRACTID  = CI.ID 
WHERE 
    CI.BASERECORD = 1 'ALWAYS GET INFORMATION FROM THE BASE RECORD

但是在Oracle SQL中我不能,因为它没有top 1函数。

我也不能使用rownum或row_number,因为Oracle不允许我这样将主查询列的值传递给嵌套子查询。 (以下代码将生成“未找到列PA.PAYMENTDATE”错误)

SELECT 
PA.*,
(
SELECT DISCOUNTCODE FROM 
   (SELECT * FROM CONTRACTINFO WHERE ID = PA.CONTRACTID AND DATE < PA.PAYMENTDATE ORDER BY DATE DESC) 
WHERE ROWNUM = 1
) 
AS DISCOUNTCODE 
FROM PAYMENT PA
INNER JOIN CONTRACTINFO CI 
ON PA.CONTRACTID  = CI.ID 
WHERE CI.BASERECORD = 1 'ALWAYS GET INFORMATION FROM THE BASE RECORD

如您所指出的,Oracle不支持TOP 1 您可以在维护关联子查询的同时在Oracle中进行重写,但是最好的选择可能是删除该子查询,而只需使用已经进行的联接即可处理逻辑:

WITH cte AS (
    SELECT
        PA.*,
        COALESCE(CI.DISCOUNTCODE, 'NA') AS DISCOUNTCODE,
        ROW_NUMBER() OVER (PARTITION BY CI.ID ORDER BY CI.DATE DESC) rn
    FROM PAYMENT PA
    LEFT JOIN CONTRACTINFO CI
        ON PA.CONTRACTID = CI.ID AND
           CI.DATE < PA.PAYMENTDATE 
    WHERE
        CI.BASERECORD = 1
)

SELECT CONTRACTID, TIME, PAYMENTDATE, DISCOUNTCODE
FROM cte
WHERE rn = 1;

Oracle fetch first而不是top n ,因此等效项为:

select pa.*
     , ( select discountcode
         from   contractinfo
         where  id = pa.contractid
         and    contractdate < pa.paymentdate
         order  by contractdate desc fetch first row only ) as discountcode
from   payment pa
       join contractinfo ci
            on  pa.contractid = ci.id
where  ci.baserecord = 1;

我必须将DATE重命名为CONTRACTDATE因为DATE是SQL关键字。 另外,尽管为ANSI完整性提供了char类型,但通常不建议使用它,因为空白填充是一项非常无用的功能,会浪费空间并导致错误。

我可能会从以下内容开始:

create table contracts
( id            integer constraint contract_pk primary key
, baserecord    integer not null
, contractdate  date not null
, discountcode  varchar2(1) );

create table payments
( contractid    references contracts
, paymentseq    number(12)
, paymentdate   date default on null sysdate );

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM