簡體   English   中英

甲骨文,如何打開游標並選擇一列到一個變量中

[英]Oracle, how to open cursor and select one column of many into a variable

我有一個Oracle存儲過程返回參考游標。 我想打開游標,然后再返回以檢查計數並在需要時引發異常,但是我在語法以及如何執行此操作方面遇到麻煩。

      V_ASN_COUNT           NUMBER;

      OPEN O_CURSOR FOR            
         SELECT  column1, -- a bunch of columns
                 column2,          
                 COUNT(DISTINCT SI.ASN_NO) OVER (PARTITION BY SI.ASN_NO) AS ASN_COUNT   
           FROM AN_ORDER_INFO OI, AN_SHIPMENT_INFO SI
          WHERE -- a bunch of criteria    

      OPEN O_CURSOR;
          LOOP
            FETCH ASN_COUNT INTO V_ASN_COUNT;
          END LOOP;
      CLOSE O_CURSOR;  

      IF(V_ASN_COUNT > 1) THEN
        RAISE MULTIPLE_ASNS;
      END IF;  

我認為您可以做到這一點:

    curid NUMBER;
    desctab DBMS_SQL.DESC_TAB;
    colcnt NUMBER; -- total number of columns
    res NUMBER;
    V_ASN_COUNT NUMBER;
BEGIN

    OPEN O_CURSOR FOR            
    SELECT 
        column1, -- a bunch of columns
        column2,
        ...
        COUNT(DISTINCT SI.ASN_NO) OVER (PARTITION BY SI.ASN_NO) AS ASN_COUNT                                 
    FROM AN_ORDER_INFO OI, AN_SHIPMENT_INFO SI
    WHERE -- a bunch of criteria    

    curid := DBMS_SQL.TO_CURSOR_NUMBER (O_CURSOR);

    DBMS_SQL.DESCRIBE_COLUMNS(curid, colcnt, desctab);
    -- "ASN_COUNT" is the last column, i. e. "colcnt" refers to column number of "ASN_COUNT"
    -- or set colcnt directly, e.g. colcnt := 12;

    FOR i IN 1..colcnt LOOP
        IF desctab(i).col_type = 2 THEN
            DBMS_SQL.DEFINE_COLUMN(curid, i, V_ASN_COUNT);
        ELSIF desctab(i).col_type = 12 THEN
            DBMS_SQL.DEFINE_COLUMN(curid, i, datevar);
            .......
        ELSE
            DBMS_SQL.DEFINE_COLUMN(curid, i, namevar, 25);
        END IF;         
    END LOOP;
    -- I do not know if this loop is needed, perhaps you can simply do 
    -- DBMS_SQL.DEFINE_COLUMN(curid, colcnt, V_ASN_COUNT);
    -- for a single column

    res := DBMS_SQL.FETCH_ROWS(curid); -- Fetch only the first row, no loop required
    DBMS_SQL.COLUMN_VALUE(curid, colcnt, V_ASN_COUNT); -- Loop over all column not required, you just like to get the last column

    IF V_ASN_COUNT > 1 THEN
      RAISE MULTIPLE_ASNS;
    END IF;
    DBMS_SQL.CLOSE_CURSOR(curid);

有關更多詳細信息,請查看Oracle文檔: DBMS_SQL.TO_CURSOR_NUMBER函數。

但是,打開/后退游標的問題仍然存在!

在上一個問題之后,如果您想多次打開同一光標以對其進行計數,則可以執行以下操作:

CREATE OR REPLACE PROCEDURE YOUR_PROC(O_CURSOR OUT SYS_REFCURSOR) is
  ASN_NO NUMBER; -- have to define all columns the cursor returns

  V_CHECK_ASN_NO NUMBER;

  -- local function to generate the cursor, to avoid repeating the text
  -- or using dynamic SQL
  FUNCTION GET_CURSOR RETURN SYS_REFCURSOR IS
    V_CURSOR SYS_REFCURSOR;
  BEGIN
    OPEN V_CURSOR FOR
      SELECT *
      FROM AN_ORDER_INFO OI, AN_SHIPMENT_INFO SI
      -- where bunch of stuff
    RETURN V_CURSOR;
  END;
BEGIN
  -- open the cursor for your check; might be better to have a local
  -- variable for this rather than touching the OUT parameter this early
  O_CURSOR := GET_CURSOR;
  LOOP
    FETCH O_CURSOR INTO ASN_NO; -- and all other columns!
    EXIT WHEN O_CURSOR%NOTFOUND;
    IF V_CHECK_ASN_NO IS NOT NULL AND V_CHECK_ASN_NO != ASN_NO THEN
      -- means we have two distinct values
      CLOSE O_CURSOR;
      RAISE MULTIPLE_ASNS;
    END IF;
    V_CHECK_ASN_NO := ASN_NO;
  END LOOP;
  -- close the check version of the cursor
  CLOSE O_CURSOR;

  -- re-open the cursor for the caller
  O_CURSOR := GET_CURSOR;
END YOUR_PROC;

您可以使用動態SQL使用相同的SQL字符串兩次打開游標,但是此版本使用本地函數使游標SQL變為靜態(並因此在編譯時進行了解析)。

游標執行兩次,並且從第一次執行中至少獲取了一些行(如果沒有重復,則為所有行;如果有重復,則可能不是所有行都被提取)。 調用方將獲得包含所有行的新結果集。

如何確保第一行可用於驗證消耗?

此代碼僅打開一次游標-沒有並發問題。 游標的前兩行都代表預期結果集的第一行-獲取第一個副本以進行驗證,如果驗證成功,則返回其余的行。

您仍然必須獲取所有列。

  V_ASN_COUNT           NUMBER;

  OPEN O_CURSOR FOR            
     WITH qry AS ( SELECT  column1, -- a bunch of columns
                           column2,          
                           COUNT(DISTINCT SI.ASN_NO) OVER (PARTITION BY SI.ASN_NO) AS ASN_COUNT   
                   FROM AN_ORDER_INFO OI, AN_SHIPMENT_INFO SI
                   WHERE -- a bunch of criteria 
                   )
     SELECT * 
     FROM   qry 
     WHERE rownum = 1
     UNION ALL
     SELECT *
     FROM   qry;

  -- Consume the expendable first row.
  FETCH O_CURSOR INTO V_ASN_COUNT; -- and all the other columns!

  IF(V_ASN_COUNT > 1) THEN
    CLOSE O_CURSOR;
    RAISE MULTIPLE_ASNS;
  END IF;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM