【问题标题】:How to replace nested table with Oracle SQL function or stored procedure如何用 Oracle SQL 函数或存储过程替换嵌套表
【发布时间】:2018-12-21 05:26:20
【问题描述】:

在Oracle中,如果我有以下两个表

CREATE TABLE Test_Persons_A (
    PersonID int,
    LastName varchar(255),
    FirstName varchar(255)
);

INSERT INTO Test_Persons_A
    (PersonID,LastName,FirstName)
    values(11,'LN_A1','FN_A1');

INSERT INTO Test_Persons_A
    (PersonID,LastName,FirstName)
    values(12,'LN_A2','FN_A2');

CREATE TABLE Test_Persons_B (
    PersonID int,
    LastName varchar(255),
    FirstName varchar(255)
);

INSERT INTO Test_Persons_B
    (PersonID,LastName,FirstName)
    values(21,'LN_B1','FN_B1');

INSERT INTO Test_Persons_B
    (PersonID,LastName,FirstName)
    values(22,'LN_B2','FN_B2');

commit;

我可以对联合进行如下查询:

with
UNIONED as
(    
    --block A to replaced by calling a function or stored proc---
    select PersonID as PID, LastName as LN, FirstName as FN
        from Test_Persons_A tp
        where tp.LASTNAME like '%1%'

    union

    --block B to replaced by calling a function or stored proc---
    select PersonID as PID, LastName as LN, FirstName as FN
        from Test_Persons_B tp
        where tp.LASTNAME like '%2%'
)

select * from UNIONED; 

我的问题是如何实现以下目标:

with
UNIONED as
(    
    --block A replaced---
    myFuncOrStoredProc('Test_Persons_A', '%1%');

    union

    --block B replaced---
    myFuncOrStoredProc('Test_Persons_B', '%2%');
)

select * from UNIONED; 

有很多关于“如何从函数或存储过程返回表(或多行数据)”的帖子。但是由于答案众多,我感到更加困惑。

对于这样一个小而简单(我认为很常见)的场景和目的,什么是最干净的方法?(特别是,我更喜欢以避免必须为返回的表指定每一列的类型。)

【问题讨论】:

    标签: sql oracle stored-procedures stored-functions


    【解决方案1】:

    Oracle 设置

    CREATE TABLE Test_Persons_A ( PersonID, LastName, FirstName ) AS
      SELECT 11, 'LN_A1', 'FN_A1' FROM DUAL UNION ALL
      SELECT 12, 'LN_A2', 'FN_A2' FROM DUAL;
    
    CREATE TABLE Test_Persons_B ( PersonID, LastName, FirstName ) AS
     SELECT 21, 'LN_B1', 'FN_B1' FROM DUAL UNION ALL
     SELECT 22, 'LN_B2', 'FN_B2' FROM DUAL;
    
    CREATE TYPE test_obj IS OBJECT(
      PersonID INT,
      FirstName VARCHAR2(255),
      LastName VARCHAR2(255)
    );
    /
    
    CREATE TYPE test_tbl IS TABLE OF test_obj;
    /
    
    CREATE OR REPLACE FUNCTION test_fn(
      table_name VARCHAR2,
      lastNameFilter VARCHAR2
    ) RETURN test_tbl
    IS
      v_tbl test_tbl;
    BEGIN
      IF UPPER( table_name ) = 'TEST_PERSONS_A' THEN
        SELECT TEST_OBJ( PersonID, LastName, FirstName )
        BULK COLLECT INTO v_tbl
        FROM   test_persons_a
        WHERE  LastName LIKE lastNameFilter;
      ELSIF UPPER( table_name ) = 'TEST_PERSONS_B' THEN
        SELECT TEST_OBJ( PersonID, LastName, FirstName )
        BULK COLLECT INTO v_tbl
        FROM   test_persons_b
        WHERE  LastName LIKE lastNameFilter;
      END IF;
      RETURN v_tbl;
    END;
    /
    

    查询 1 在单个表集合表达式中使用 MULTSET UNION [ALL|DISTINCT]

    SELECT *
    FROM   TABLE(
             test_fn( 'TEST_PERSONS_A', '%1%' )
             MULTISET UNION ALL
             test_fn( 'TEST_PERSONS_B', '%2%' )
           );
    

    查询 2 使用UNION [ALL] 和多个表集合表达式:

    SELECT * FROM TABLE( test_fn( 'TEST_PERSONS_A', '%1%' ) )
    UNION ALL
    SELECT * FROM TABLE( test_fn( 'TEST_PERSONS_B', '%2%' ) );
    
    两个查询的

    输出

      PERSONID FIRSTNAME LASTNAME
    ---------- --------- --------
            11 LN_A1     FN_A1
            22 LN_B2     FN_B2
    

    【讨论】:

    • 非常感谢 MT0。所以即使我只想返回某个表的相同结构的表,我也无法避免为每个列定义一个表? (例如,如果我想从 Test_Persons 返回 select *)
    • @Stochastika 您可以返回具有与表相同结构的游标,而无需定义保存数据的对象,但您不能合并两个游标。您可以合并两个集合,但如果您的集合包含具有多列的对象,那么您需要定义具有这些列的对象。也许我不理解您要表达的观点,但您不能使用联合并且不知道列。
    • @MTO,我刚刚注意到,您的答案中函数内选择的逻辑主体:'SELECT TEST_OBJ(PersonID, LastName, FirstName) BULK COLLECT INTO v_tbl FROM test_persons_b WHERE LastName LIKE lastNameFilter ;',这里的问题是我仍然需要复制和粘贴吗?如果是这样,如果我希望表名成为“变量”,那么避免使用动态 SQL 的唯一选择是什么?
    • @Stochastika 如果需要,您可以使用动态 SQL - 我选择将有效表名列入白名单并使用静态 SQL,因为它比动态 SQL 更容易受到攻击。
    • 这是让我困惑了一段时间的一件事:没有其他方法可以在代码块中使用类似于您上面的表的变量吗?尤其是在这种情况下,如果我们知道表 TEST_PERSONS_A 和 TEST_PERSONS_B 具有完全相同的结构?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-26
    • 2014-01-24
    • 2011-06-01
    • 2013-01-21
    • 2020-11-14
    • 2020-10-12
    • 1970-01-01
    相关资源
    最近更新 更多