簡體   English   中英

在 CTE 中使用用戶定義的函數

[英]Using a user-defined function within a CTE

我有一個簡單的函數,用於計算 Oracle 數據庫中數據幀中的行數( 這里是本教程中的回收代碼)。

CREATE OR REPLACE FUNCTION TABCOUNT (tbl_name IN VARCHAR2)
   RETURN PLS_INTEGER
IS
   retval   PLS_INTEGER;
BEGIN
   EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || tbl_name INTO retval;

   RETURN retval;
END;

如果我想將此函數應用於存儲表,它執行得很好:

CREATE TABLE EXAMPLE_TABLE ("ROW_ID" INT);
INSERT INTO EXAMPLE_TABLE VALUES (1);
INSERT INTO EXAMPLE_TABLE VALUES (2);

SELECT TABCOUNT('EXAMPLE_TABLE') AS "N_ROW" FROM DUAL;

但是,我想在 CTE 中使用這種類型的函數,如下所示。

WITH TABLE_1 AS (SELECT * FROM EXAMPLE_TABLE)
SELECT 'T1' AS "TABLE_NAME", TABCOUNT (TABLE_1) AS "N_ROW"
  FROM DUAL;

出於某種原因,我無法運行它。 有人可以告訴我怎么做嗎? 如果這是一個基本問題,我很抱歉,因為我對 PL/SQL 函數很陌生。

我想在 CTE 中執行此操作的簡化原因是,使用上面的示例,我想將其應用於 CTE 中的各種不同表並合並結果,即:

編輯:在下面的示例中,“EXAMPLE_TABLE”和“EXAMPLE_TABLE_2”是占位符。 假設 TABLE_1 和 TABLE_2 是進一步處理查詢所產生的中間表。 包含 'EXAMPLE_TABLE' 和 'EXAMPLE_TABLE_2' 只是為了使示例中的 CTE 運行。

CREATE TABLE EXAMPLE_TABLE_2 AS SELECT * FROM EXAMPLE_TABLE;

WITH TABLE_1 AS (SELECT * FROM EXAMPLE_TABLE),
     TABLE_2 AS (SELECT * FROM EXAMPLE_TABLE_2)
SELECT 'T1' AS "TABLE_NAME", TABCOUNT (TABLE_1) AS "N_ROW" FROM DUAL
UNION ALL
SELECT 'T2' AS "TABLE_NAME", TABCOUNT (TABLE_2) AS "N_ROW" FROM DUAL;

如果我理解你是正確的,那么你需要像下面這樣的東西,

with table_list 
as 
( select 'EXAMPLE_TABLE' table_name from dual union all
  select 'EXAMPLE_TABLE_2' from dual
)
select t.table_name,tabcount(table_name)  no_of_rows
  from table_list t

但是,如果您使用的是 12c 或更高版本,您也可以在 WITH 子句中使用 PL/SQL 聲明部分,

with 
function tabcount (tbl_name in varchar2)
return pls_integer 
is
  retval pls_integer;
begin
   execute immediate 'select count(*) from ' || tbl_name into retval;
   return retval;
end;
table_list 
as 
( select 'EXAMPLE_TABLE' table_name from dual union all
  select 'EXAMPLE_TABLE_2' from dual
)
select t.table_name,tabcount(table_name)  no_of_rows
  from table_list t

有關此處的WITH子句的更多詳細信息

在您執行TABCOUNT (TABLE_1)時的查詢中,唯一的TABLE_1是 CTE; 您需要引用該 CTE 中的列而不是表本身,這不是您想要的(因為row_id的值不是您打算作為tbl_name傳遞的值)。 CTE 將返回值為 1 和 2 的兩行,而不是它們來自的表的名稱。 但這是學術性的,因為查詢不使用 CTE - 您正在查詢dual

您可以將表名聯合在一起:

SELECT 'EXAMPLE_TABLE' AS "TABLE_NAME", TABCOUNT ('EXAMPLE_TABLE') AS "N_ROW" FROM DUAL
UNION ALL
SELECT 'EXAMPLE_TABLE_2' AS "TABLE_NAME", TABCOUNT ('EXAMPLE_TABLE_2') AS "N_ROW" FROM DUAL;

但大概你不想復制那么多代碼,特別是如果你有很多表要查看; 因此,如果您想使用 CTE,那么請生成名稱:

WITH CTE (TBL_NAME) AS (
  SELECT 'EXAMPLE_TABLE' FROM DUAL
  UNION ALL
  SELECT 'EXAMPLE_TABLE_2' FROM DUAL
)
SELECT TBL_NAME AS "TABLE_NAME", TABCOUNT (TBL_NAME) AS "N_ROW" FROM CTE;

或者為了避免 CTE 中的重復,請使用集合:

SELECT COLUMN_VALUE AS "TABLE_NAME", TABCOUNT (COLUMN_VALUE) AS "N_ROW"
FROM TABLE(sys.odcivarchar2list('EXAMPLE_TABLE', 'EXAMPLE_TABLE_2'));

您可能會發現這個基於 XML 的技巧很有用,因為它不需要該函數; 適應使用相同的集合方法來提供名稱並避免不推薦使用的函數:

select column_value as table_name,
  to_number(xmlquery('/ROWSET/ROW/C/text()'
    passing xmltype(dbms_xmlgen.getxml(
      'select count(*) as c from "' || column_value || '"'))
    returning content)) as n_row
from table(sys.odcivarchar2list('EXAMPLE_TABLE', 'EXAMPLE_TABLE_2'));

數據庫<>小提琴


在下面的示例中,“EXAMPLE_TABLE”和“EXAMPLE_TABLE_2”是占位符。 假設 TABLE_1 和 TABLE_2 是進一步處理查詢所產生的中間表。

我認識到我可以直接將函數應用於表,但在實際用例中,TABLE_1 和 TABLE_2 是進一步處理 CTE 的最終結果的表(而不僅僅是EXAMPLE_TABLE 和EXAMPLE_TABLE_2 的鏡像副本)。

您不需要函數或動態 SQL 來執行此操作; 事實上你不能,因為 CTE 將超出動態 SQL 上下文的范圍。 但是你可以做一個簡單的計數:

WITH TABLE_1 AS (SELECT * FROM EXAMPLE_TABLE),
     TABLE_2 AS (SELECT * FROM EXAMPLE_TABLE_2)
SELECT 'T1' AS "TABLE_NAME", COUNT(*) AS "N_ROW" FROM TABLE_1
UNION ALL
SELECT 'T2' AS "TABLE_NAME", COUNT(*) AS "N_ROW" FROM TABLE_2;

數據庫<>小提琴

問題是您引用TABLE_1TABLE_2就好像它是一個列,但它是一個表。 您需要像下面的示例查詢一樣將表的名稱傳遞給函數。

詢問

WITH
    table_list (table_name)
    AS
        (SELECT 'ALL_TABLES' FROM DUAL
         UNION ALL
         SELECT 'ALL_OBJECTS' FROM DUAL)
SELECT table_name, TABCOUNT (table_name) AS N_ROW
  FROM table_list;

改進的功能

如果您確實打算實現這樣的功能,我建議您進行一些保護以防止 SQL 注入,因為目前的方式非常容易受到攻擊。 請參閱下面我的改進版本。

CREATE OR REPLACE FUNCTION TABCOUNT (p_table_name   all_tables.table_name%TYPE,
                                     p_owner        all_tables.owner%TYPE DEFAULT NULL)
    RETURN PLS_INTEGER
IS
    retval   PLS_INTEGER;
BEGIN
    EXECUTE IMMEDIATE   'SELECT COUNT(*) FROM '
                     || CASE
                            WHEN p_owner IS NOT NULL THEN DBMS_ASSERT.schema_name (p_owner) || '.'
                        END
                     || DBMS_ASSERT.sql_object_name (p_table_name)
        INTO retval;

    RETURN retval;
END;
/

暫無
暫無

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

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