【问题标题】:How to populate a PL/SQL Object Type with encapsulated Object Types or Collection Types using a single SQL Query如何使用单个 SQL 查询使用封装的对象类型或集合类型填充 PL/SQL 对象类型
【发布时间】:2019-01-29 08:49:23
【问题描述】:

我目前正在使用父对象类型中的嵌套对象类型和集合类型来设置数据模型,并且需要一种方法来使用单个 SQL 查询来填充对象类型,包括子对象。

作为使用 HR 模式的示例,我创建了一个示例数据结构。目标是获取所有部门的集合,其中包含在该部门工作的员工列表。

我使用 Oracle SQL Database 12.1 创建了数据结构:

CREATE OR REPLACE TYPE employee_ot AS OBJECT ( 
    employee_id   NUMBER,
    first_name    VARCHAR2(100),
    last_name     VARCHAR2(100)
)
create or replace TYPE EMPLOYEE_CT AS TABLE OF employee_ot;
create or replace TYPE DEPARTMENT_OT AS OBJECT 
( 
    department_id number,
    employees employee_ct
)
create or replace TYPE DEPARTMENT_CT AS TABLE OF DEPARTMENT_OT;

只需使用以下方法填充 employee_ct 即可:

DECLARE
    v_employee_ct   employee_ct;
BEGIN
    SELECT
        employee_ot(employee_id,first_name,last_name)
    BULK COLLECT INTO
        v_employee_ct
    FROM
        emp_details_view;

    dbms_output.put_line(v_employee_ct.count);
    FOR employee_index IN v_employee_ct.first..v_employee_ct.last LOOP
        dbms_output.put_line(v_employee_ct(employee_index).employee_id
        || ' '
        || v_employee_ct(employee_index).first_name
        || ' '
        || v_employee_ct(employee_index).last_name);
    END LOOP;

END;

当我尝试使用类似查询填充 department_ct 时,出现错误:

DECLARE
    v_department_ct   department_ct;
BEGIN
    select department_ot(ev1.department_id, 
    (SELECT
        employee_ot(ev2.employee_id,ev2.first_name, ev2.last_name)
    BULK COLLECT INTO
        v_employee_ct
    FROM
        emp_details_view ev2
    where ev2.department_id = ev1.department_id))
    bulk collect into v_department_ct
    from emp_details_view ev1 
    group by department_id;
END;
PL/SQL: ORA-01744: inappropriate INTO

删除我得到的第二个批量收集到子句:

DECLARE
    v_department_ct   department_ct;
BEGIN
    select department_ot(ev1.department_id, 
    (SELECT
        employee_ot(ev2.employee_id,ev2.first_name, ev2.last_name)
    FROM
        emp_details_view ev2
    where ev2.department_id = ev1.department_id))
    bulk collect into v_department_ct
    from emp_details_view ev1 
    group by department_id;
END;
PL/SQL: ORA-00932: inconsistent datatypes: expected HR.EMPLOYEE_OT got HR.EMPLOYEE_CT

使用函数获取指定department_id 的employee_ct 是可行的。但是,我担心在大型数据集上使用它时会出现性能问题:

CREATE OR REPLACE FUNCTION get_employees (
    department_id_in NUMBER
) RETURN employee_ct AS
    v_employee_ct   employee_ct;
BEGIN
    v_employee_ct := employee_ct ();
    SELECT
        employee_ot(employee_id,first_name,last_name)
    BULK COLLECT INTO
        v_employee_ct
    FROM
        emp_details_view
    WHERE
        department_id = department_id_in;

    RETURN v_employee_ct;
END get_employees;
DECLARE
    v_department_ct   department_ct;
BEGIN
    SELECT
        department_ot(ev1.department_id, (get_employees(ev1.department_id) ) )
    BULK COLLECT INTO
        v_department_ct
    FROM
        emp_details_view ev1
    GROUP BY
        department_id;
    FOR department_index IN v_department_ct.first..v_department_ct.last LOOP
        DECLARE
            v_department_ot   department_ot;
        BEGIN
            v_department_ot := v_department_ct(department_index);
            dbms_output.put_line(v_department_ot.department_id || ' ' ||v_department_ot.employees.count);
            FOR employee_index IN v_department_ot.employees.first..v_department_ot.employees.last LOOP
                DECLARE
                    v_employee_ot   employee_ot;
                BEGIN
                    v_employee_ot := v_department_ot.employees(employee_index);
                    dbms_output.put_line(v_employee_ot.employee_id
                    || ' '
                    || v_employee_ot.first_name
                    || ' '
                    || v_employee_ot.last_name);

                END;
            END LOOP;

        END;
    END LOOP;

END;

有什么方法可以避免使用函数,而是在单个查询中编写封装对象的创建?

【问题讨论】:

    标签: sql oracle plsql encapsulation object-type


    【解决方案1】:

    您可以使用multiset 运算符并将数据转换为employee_ct。这个代码块对我有用:

    declare
        v_department_ct   department_ct;
    begin
      select department_ot(ev1.department_id, 
             cast(multiset(select employee_ot(ev2.employee_id,ev2.first_name, ev2.last_name) 
                             from emp_details_view ev2 
                             where ev2.department_id = ev1.department_id ) 
                  as employee_ct))
        bulk collect into v_department_ct
        from emp_details_view ev1 
        group by department_id;
    end;
    

    我的测试数据:

    create table emp_details_view (department_id, employee_id, first_name, last_name) as (
      select 1, 101, 'A', 'A' from dual union all
      select 1, 102, 'B', 'B' from dual union all
      select 2, 201, 'X', 'X' from dual )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-03
      • 1970-01-01
      • 2011-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多