[英]Equivalent of SQL Server's “TOP 1” in Oracle (without using rownum or row_number())
I have a CONTRACTINFO
table which stores contract's discount code (the code can change from time to time, depending on the type of the contract or the period). 我有一个
CONTRACTINFO
表,该表存储合同的折扣代码(该代码可以不时更改,具体取决于合同的类型或期限)。
CREATE TABLE CONTRACTINFO
(
ID CHAR(8),
BASERECORD CHAR(1),
DATE CHAR(8),
DISCOUNTCODE CHAR(1)
)
Every month, we need to calculate the fee the customers have to pay based on paymentdate
and discountcode
. 每个月,我们需要根据付款
paymentdate
和discountcode
计算客户必须支付的费用。
CREATE TABLE PAYMENT
(
CONTRACTID CHAR(8),
TIME NUMBER(12),
PAYMENTDATE CHAR(8)
)
The discount code is determined by getting the last record which has date
< paymentdate
from CONTRACTINFO
table. 折扣代码是通过从
CONTRACTINFO
表中获取date
< paymentdate
的最后一条记录来确定的。
I have created a simple example to show the desired result (in yellow). 我创建了一个简单的示例来显示所需的结果(以黄色显示)。
In SQL Server I can easily achieve this using below correlated subquery: 在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
But in Oracle SQL I can't because it doesn't have a top 1 function. 但是在Oracle SQL中我不能,因为它没有top 1函数。
I can't use rownum or row_number also because Oracle doesn't allow me to pass value from main query's column into nested sub query like this. 我也不能使用rownum或row_number,因为Oracle不允许我这样将主查询列的值传递给嵌套子查询。 (the below code will generate "column PA.PAYMENTDATE not found" error)
(以下代码将生成“未找到列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 does not support TOP 1
, as you pointed out. 如您所指出的,Oracle不支持
TOP 1
。 You might be able to rewrite in Oracle while maintaining the correlated subquery, but the best option would probably be to remove that subquery, and instead just use the join you already making to handle the logic: 您可以在维护关联子查询的同时在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 has fetch first
instead of top n
, so the equivalent would be: 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;
I had to rename DATE
to CONTRACTDATE
because DATE
is a SQL keyword. 我必须将
DATE
重命名为CONTRACTDATE
因为DATE
是SQL关键字。 Also, although the char
type is provided for ANSI completeness, it is not generally a good idea to use it because blank-padding is a rather pointless feature that wastes space and leads to bugs. 另外,尽管为ANSI完整性提供了
char
类型,但通常不建议使用它,因为空白填充是一项非常无用的功能,会浪费空间并导致错误。
I would probably start with something like this: 我可能会从以下内容开始:
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.