簡體   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