[英]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.