1

我刚刚从 SQL Server 迁移到 ORACLE,并且正在编写一个脚本(SQL Server 风格)。在 SQL Server 中,我们在脚本中应用一些逻辑后使用 BEING TRAN - END TRAN。这可以在 BEGIN-END 块内完成。在 Oracle 中,我发现这有点困难。在这个网站上进行了大量的谷歌搜索和搜索后,我仍然不清楚如何满足这个要求。

当我运行脚本(粘贴在下面)时,它作为一个事务运行。并且 DBMS_OUTPUT.Put_line 也会在整个脚本执行后显示。有没有办法在每次提交后打印 DBMS_OUTPUT.Put_line ?

另外,我愿意接受想法,如果有任何其他方法可以处理这个脚本,以便在脚本移动到下一个子查询之前提交每个子查询......请让我知道。

这是我的脚本:

SET SERVEROUTPUT ON;
--spool Consolidated.log;
WHENEVER SQLERROR EXIT SQL.SQLCODE;
SET DEFINE OFF;

ALTER SESSION SET GLOBAL_NAMES=FALSE;

DECLARE
ExtractType         NUMBER(9);
RecordsExtracted    NUMBER(9);
CurStatus           NUMBER(9);
StartDate           date;
ErrorMessage        NVARCHAR2(1000);
LastExtrctTimestamp DATE;


BEGIN    
    -- AgreementTradeTypes
    StartDate := sysdate;
    ExtractType := 44;

    DELETE FROM AgreementTradeTypes;

    INSERT INTO AgreementTradeTypes (AgreementId,AgreementName,PrincipalId,Principal,CounterpartyId,Counterparty, TradeTypeId,TradeTypeName,BusinessLine,AdditionalCriteria)
    -- From CORE DB 
    SELECT  
            IATT.AgreementId, AG.AgreementName, IATT.PRINCIPALID, Principal.ENTITYNAME Principal, 
            IATT.COUNTERPARTYID, Cpty.ENTITYNAME Counterparty, 
            IATT.TradeTypeID, TT.TradeTypeName, BusLine.ENUMTEXT BusinessLine, IATT.ADDITIONALCRITERIA
    FROM    IncludedAgreementTradeTypes@RPTCORE IATT 
            INNER JOIN Entities@RPTCORE Principal ON IATT.PRINCIPALID = Principal.ENTITYID
            INNER JOIN Entities@RPTCORE Cpty ON IATT.CounterpartyId = Cpty.ENTITYID
            INNER JOIN EnumValues@RPTCORE BusLine ON IATT.BusinessLine = BusLine.ENUMVALUE AND BusLine.ENUMTYPE = 'BusinessLine'
            INNER JOIN Agreements@RPTCORE AG ON IATT.AGREEMENTID = AG.AgreementID
            INNER JOIN TradeTypes@RPTCORE TT ON IATT.TRADETYPEID = TT.TradeTypeID
    ORDER BY IATT.AgreementId;
    RecordsExtracted := SQL%RowCount;

    DBMS_OUTPUT.put_line('AgreementTradeTypes Records Extracted:' || RecordsExtracted);       

    -- On Success
    CurStatus := 2;
    ErrorMessage := 'AgreementTradeTypes Complete';

    INSERT INTO ExtractRecords(ExtractType, RecordsExtracted, Status, ExtractTimestamp, StartDate, EndDate, ErrorMessage)
    VALUES (ExtractType, RecordsExtracted, CurStatus, SysDate, StartDate, SysDate, ErrorMessage);

    INSERT INTO LoadRecords (LoadType,Status,LoadTimestamp,StartDate,EndDate) 
    VALUES (ExtractType, CurStatus, SysDate, StartDate, SysDate);

    COMMIT; /* Committing first Block */

    -- INTEREST PAYMENT PERIODS 
    StartDate := sysdate;
    ExtractType := 57;

    DELETE FROM InterestPaymentPeriods;

    INSERT INTO InterestPaymentPeriods (InterestPaymentPeriodId,AgreementId,AgreementName,CurrencyId,CurrencyName,InstrumentId,InstrumentName,PositionType,CollateralMarginType,PeriodStart,PeriodEnd,NextPeriodEnd,AccruedInterest,OpeningBalance,EndingBalance,MarketIndexId,MarketIndexName,Spread,DayCountConvention,CalculationType,ManagingLocation,BusinessLine)
    -- From CORE DB 
    SELECT
            IPP.INTERESTPAYMENTPERIODID, IPP.AGREEMENTID, AG.AGREEMENTNAME, IPP.CURRENCYID, CUR.CODE CurrencyName, IPP.INSTRUMENTID,
            Instruments.DESCRIPTION InstrumentName, PosType.ENUMTEXT PositionType, CollMargType.ENUMTEXT CollateralMarginType,
            IPP.PERIODSTART, IPP.PERIODEND, IPP.NEXTPERIODEND, IPP.ACCRUEDINTEREST, IPP.OPENINGBALANCE, IPP.ENDINGBALANCE,
            IPP.MARKETINDEXID, MI.MARKETINDEXNAME, IPP.SPREAD, DCC.ENUMTEXT DayCountConvention, CalcType.ENUMTEXT CalculationType,
            Cty.CITYNAME ManagingLocation, BusLine.ENUMTEXT BusinessLine
    FROM
            INTERESTPAYMENTPERIODS@RPTCORE IPP
            INNER JOIN Agreements@RPTCORE AG ON IPP.AGREEMENTID = AG.AGREEMENTID
            LEFT OUTER JOIN Currencies@RPTCORE CUR ON IPP.CURRENCYID = CUR.CURRENCYID
            LEFT OUTER JOIN Cities@RPTCORE Cty ON IPP.MANAGINGLOCATIONID = Cty.CITYID
            LEFT OUTER JOIN MarketIndexes@RPTCORE MI ON IPP.MARKETINDEXID = MI.MARKETINDEXID
            LEFT OUTER JOIN Instruments@RPTCORE ON IPP.INSTRUMENTID = Instruments.INSTRUMENTID
            LEFT OUTER JOIN EnumValues@RPTCORE PosType ON IPP.POSITIONTYPE = PosType.ENUMVALUE AND PosType.ENUMTYPE = 'PositionType'
            LEFT OUTER JOIN EnumValues@RPTCORE CollMargType ON IPP.COLLATERALMARGINTYPE = CollMargType.ENUMVALUE AND CollMargType.ENUMTYPE = 'CollateralMarginType'
            LEFT OUTER JOIN EnumValues@RPTCORE DCC ON MI.DAYCOUNTCONVENTION = DCC.ENUMVALUE AND DCC.ENUMTYPE = 'DayCountConvention'
            LEFT OUTER JOIN EnumValues@RPTCORE CalcType ON IPP.CALCULATIONTYPE = CalcType.ENUMVALUE AND CalcType.ENUMTYPE = 'CalculationType'
            LEFT OUTER JOIN EnumValues@RPTCORE BusLine ON IPP.BUSINESSLINE = BusLine.ENUMVALUE AND BusLine.ENUMTYPE = 'BusinessLine';
    RecordsExtracted := SQL%RowCount;       

    DBMS_OUTPUT.put_line('InterestPaymentPeriods Records Extracted:' || RecordsExtracted);

    -- On Success
    CurStatus := 2;
    ErrorMessage := 'Interest_Payment_Periods Complete';

    INSERT INTO ExtractRecords(ExtractType, RecordsExtracted, Status, ExtractTimestamp, StartDate, EndDate, ErrorMessage)
    VALUES (ExtractType, RecordsExtracted, CurStatus, SysDate, StartDate, SysDate, ErrorMessage);

    INSERT INTO LoadRecords (LoadType,Status,LoadTimestamp,StartDate,EndDate) 
    VALUES (ExtractType, CurStatus, SysDate, StartDate, SysDate);

    COMMIT; /* Committing Second Block */
END;

--spool off;
/
4

3 回答 3

5

Oracle 中通常的做法是仅在业务事务完成时才提交,这样事务就不会被部分处理。这与其他一些系统不同,因为 Oracle 的多版本和锁定系统确保写入者不会阻塞读取器,而读取器不会阻塞写入器。

对于 DBMS_Output 问题,不,您无法在块执行的中途通过 DBMS_Output 从服务器获得响应。您可能希望查看使用 Utl_File 将数据写入服务器端文件。

其他想法:

  • Consider using TRUNCATE instead of DELETE if you're deleting every row AND you do not need to use foreign keys against that table. TRUNCATE has an implicit commit associated with it, so apply all your truncates at the beginning of the procedure.

  • Consider using the APPEND hint on the inserts to invoke direct path insert if you are loading bulk data, have indexes to maintain, and do not need to allow multiple simultaneous inserts into the table.

  • I'm guessing that the ORDER BY on the inserts is there for a reason -- usually in Oracle it would be to ensure that data rows are physically clustered on the order-by column(s), which leads to greater efficiency on index-based access via those columns. Using a direct path insert would help guarantee physical row ordering, but if you don't need that clustering then remove the ORDER BY

于 2013-02-18T10:50:35.540 回答
1

The data will be committed in two stages. The DECLARE/BEGIN/END denote the start and end of an anonymous PL/SQL block, not a transaction. If you didn't have a commit inside the block (which would be more normal; it's somewhat unusual to have transaction control inside a block) at all, then after the block completes none of your changes would have been committed - you could still roll back if you wanted to.

Your DBMS_OUTPUT calls put messages into a buffer, which the client (e.g SQL*Plus) retrieves and displays after the block completes. There is no way around that - you can't get 'live' updates using that mechanism. There are alternatives, such as using UTL_FILE or setting module information for the session that can be viewed from another session, but that's probably overkill for what you seem to be doing.

If you just want to see the message after each section you can split it into two anonymous blocks. The downside of that is perhaps having to declare variables twice, and that they will go out of scope between the blocks - so something you set in the first block won't be visible in the second. You could perhaps work around that using bind variables, via the SQL*Plus variable command.

You don't really seem to need to do this in PL/SQL at all, at least in this example, though you said you would be applying some logic in the script at well. If that's not really the case then you could use simple SQL statements, substitution variables and prompt to achieve this, without using PL/SQL at all. Apart from the row counts, which are a little tricky unless you're happy just with the normal set feedback on display, like '3 rows inserted'.

于 2013-02-18T10:52:20.403 回答
0

The results of DBMS_OUTPUT will only be displayed after completion of the PL/SQL block. DBMS_OUTPUT and COMMIT are not related to one another. If you can split your script into multiple blocks you can get the results of each block committed and printed before the next block starts.

begin
   -- step 1
   insert 
   commit ...
   dbms_output ...
end;
/

begin
   -- step 2
   insert...
   commit ...
   dbms_output ...
end;
/
于 2013-02-18T10:53:07.473 回答