如何将表作为参数传递并使用 plsql 过程填充该表

Posted

技术标签:

【中文标题】如何将表作为参数传递并使用 plsql 过程填充该表【英文标题】:How to pass table as a parameter and populate that table using plsql procedure 【发布时间】:2020-01-25 11:37:10 【问题描述】:

我想使用 plsql 填充一个表。它将获取表名作为参数并插入表中已经存在的记录。问题是当表中有主键时,不能有重复的记录。我不知道如何解决这个问题。 此代码生成 20 行并插入到特定表中。由于employee_id 是主键,我为此生成了序列。但我的问题是当我想为任何表插入记录时。 简而言之,我想执行动态插入语句。

这段代码可以很好地为员工表插入记录。

create or replace procedure proc ( number_of_records IN number )
IS
BEGIN
INSERT INTO employees (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,COMMISSION_PCT,MANAGER_ID,DEPARTMENT_ID)
SELECT generate.nextval,FIRST_NAME,LAST_NAME,DBMS_RANDOM.STRING('A', 20),PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,COMMISSION_PCT,MANAGER_ID,DEPARTMENT_ID
FROM employees where rownum<=number_of_records;
end proc;

execute proc(20);

【问题讨论】:

你不知道如何将表名作为参数传递,然后对该表执行sql语句? 是的,但我的问题更多是关于我不知道如何执行插入到作为参数给出的任何表的语句中。因为我不知道它的列和主键列。我对plsql有点陌生。 @dmak2709 您的 Oracle 版本是多少? 是10g版@Ankit Bajpai 【参考方案1】:

由于问题标记在 Oracle 下,因此假设您使用的是 Oracle 数据库。基于此,您可以探索all_tablesall_tab_columns视图,获取名称作为参数传递的表的表信息和对应的列信息。

您可以进一步浏览 Oracle 文档以获取有关可用于了解表主键的约束的相应视图。

【讨论】:

是的,我想之后我需要弄清楚如何为那个主键列实现一个序列。 @SB【参考方案2】:

我建议您可以使用以下查询在您的过程中创建一个动态查询:

以下查询将为您提供输入参数 (V_TABLE_NAME) 的表名和列

    select  aa.table_name, 
            aa.column_name  
    from    all_tab_columns aa
    where   (instr(aa.table_name, '$') = 0 
    and     aa.owner = 'V_SCHEMA_NAME')
    and     aa.table_name = 'V_TABLE_NAME'; 

使用以下查询,您可以识别该输入表名称上可用的主键

    select  aa.table_name, 
            aa.column_name ,
            ac.constraint_type
    from   all_tab_columns aa,
           ALL_CONSTRAINTS ac
    where  aa.table_name = ac.table_name
    and    (instr(aa.table_name, '$') = 0 
    and    aa.owner = 'V_SCHEMA_NAME')
    and    aa.table_name = 'V_TABLE_NAME'
    and    ac.constraint_type = 'P'; 

检查此解决方案是否适合您。如果您对此有任何疑问,请告诉我。我会尝试解决它。

谢谢。

【讨论】:

感谢@Chetan649,现在我可以获取表中的主键列。所以我现在的问题是如何运行 insert into 语句。例如插入 V_Table_Name select * from.... @s250695 - 请参阅EXECUTE IMMEDIATE的文档 当将all_tab_columns 加入到all_constraints 时,您应该在加入中包含owner。使用join 子句连接会更清晰。另外我认为不需要括号。【参考方案3】:

Oracle 12C 或更高版本现在允许使用标识列。因此,您可以在不使用序列的情况下插入多行。但是在12C之前,我猜你必须使用CURSOR来实现它-

CREATE OR REPLACE PROCEDURE PROC ( number_of_records IN NUMBER)
IS
BEGIN
    FOR REC IN (SELECT FIRST_NAME
                      ,LAST_NAME
                      ,DBMS_RANDOM.STRING('A',20) EMAIL
                      ,PHONE_NUMBER
                      ,HIRE_DATE
                      ,JOB_ID
                      ,SALARY
                      ,COMMISSION_PCT
                      ,MANAGER_ID
                      ,DEPARTMENT_ID
                FROM employees
                WHERE ROWNUM <= number_of_records)
        LOOP
             INSERT INTO employees(EMPLOYEE_ID
                                  ,FIRST_NAME
                                  ,LAST_NAME
                                  ,EMAIL
                                  ,PHONE_NUMBER
                                  ,HIRE_DATE
                                  ,JOB_ID
                                  ,SALARY
                                  ,COMMISSION_PCT
                                  ,MANAGER_ID
                                  ,DEPARTMENT_ID)
             VALUES(generate.nextval
                   ,REC.FIRST_NAME
                   ,REC.LAST_NAME
                   ,REC.EMAIL
                   ,REC.PHONE_NUMBER
                   ,REC.HIRE_DATE
                   ,REC.JOB_ID
                   ,REC.SALARY
                   ,REC.COMMISSION_PCT
                   ,REC.MANAGER_ID
                   ,REC.DEPARTMENT_ID);
        END LOOP;
END PROC;
/
execute proc(20);

【讨论】:

是的,这是插入多行的好方法。但就我而言,现在我需要将表作为参数,因此列将是未知的。 @Ankit Bajpai【参考方案4】:

虽然您可能不喜欢它,但我建议您放弃这个想法,这是一个非常糟糕的想法。首先,因为你想将表名作为参数传递,所以你不能编写 SQL 语句;您需要动态 sql,通过 EXECUTE IMMEDIATE 或 DBMS_SQL,它本身从来都不是简单的。其次,要完全正确实施,它变得复杂变化迅速。事先你需要考虑如果:

    PK 不是自动生成的。 在 12c 及更高版本 PK 上使用 IDENTITY,但它被定义为 ON DEFAULT。 PK 是多列。 是否存在 PK 以外的唯一约束(或代替 PK)。 是否存在未定义为约束的唯一索引。 其他?

第三,动态 SQL 往往是拖累或性能。

还有一种选择,但不是很好。为每个表创建一个过程,然后您的 PROC 过程查看表名并调用适当的较低级别的过程。但这往往会很快成为维护方面的难题。 祝你好运!

【讨论】:

这个任务是从我工作的地方交给我的,所以我们看看我能不能继续下去。我同意你的观点,我也考虑过你上面提到的细节,这并不容易。我会考虑你的替代解决方案谢谢@Belayer。【参考方案5】:

请检查我的另一种方法,我认为这种方法对您有用。

    创建一个有两个参数的过程

    CREATE OR REPLACE PROCEDURE PROC ( P_TABLE_NAME IN VARCHAR2(100),P_number_of_records 
    IN NUMBER) 
    

    创建两条动态SQL

    我。游标的第一个动态 SQL

    REC_STATE_STATEMENT := SELECT 'FOR REC IN (SELECT ' || RTRIM (xmlagg (xmlelement 
    (e, COLUMN_NAME || ',')).extract ('//text()'),',') || ' FROM '||P_TABLE_NAME || ' 
    WHERE ROWNUM <= ' ||P_number_of_records||' )' COLUMN_NAMES
    FROM ALL_TAB_COLS 
    WHERE TABLE_NAME='P_TABLE_NAME' 
    GROUP BY
    TABLE_NAME;   
    

    二。用于插入的Secon动态Sql(循环执行此语句)

    INSERT_INTO_STATEMENT:SELECT 'INSERT INTO ' ||P_TABLE_NAME||' (' 
    || RTRIM (xmlagg (xmlelement (e, COLUMN_NAME || ',')).extract ('//text()'),',') 
    ||' )' ||' VALUES (' 
    || RTRIM (xmlagg (xmlelement (e, COLUMN_NAME || ',REC.')).extract 
    ('//text()'),',REC.')||' )' COLUMN_NAMES
    FROM ALL_TAB_COLS 
    WHERE TABLE_NAME='P_TABLE_NAME'
    GROUP BY
    TABLE_NAME;
    

如果这对您有帮助,请告诉我。

问候, 车坦

【讨论】:

以上是关于如何将表作为参数传递并使用 plsql 过程填充该表的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PLSQL 过程中将绑定变量作为 IN OUT 参数传递

如何在 PLSQL 过程中将字符串值作为参数传递

如何在 Oracle SQL Developer 的存储过程中将表名列表作为参数传递?如何使用 PLSQL VARRAY 或嵌套表?

如何使用动态用户输入在 plsql 中使用函数/过程创建表

如何从.net代码将表值参数传递给存储过程

如何使用 JDBC 将表值参数(类数组参数)传递给 Microsoft SQL Server 2008 R2 中的存储过程? [复制]