PL/SQL 存储过程创建表

Posted

技术标签:

【中文标题】PL/SQL 存储过程创建表【英文标题】:PL/SQL Stored Procedure create tables 【发布时间】:2016-01-22 12:35:40 【问题描述】:

我的任务是改进旧的 PL/SQL 和 Oracle SQL 遗留代码。总共有大约 7000 行代码!现有代码中真正让我感到惊讶的一个方面是,之前的编码员通过不编写任何过程或函数而不必要地创建了数百行代码 - 相反,编码员基本上自始至终重复相同的代码。

例如,在现有代码中,实际上有 40 次或更多重复以下 SQL:

CREATE TABLE tmp_clients
AS
    SELECT * FROM live.clients;

CREATE TABLE tmp_customers
AS
    SELECT * FROM live.customers;

CREATE TABLE tmp_suppliers
AS
    SELECT * FROM live.suppliers WHERE type_id = 1;

and many, many more.....

虽然我最近购买了 Steven Feuerstein 的优秀书籍“Oracle PL/SQL 编程”,但我对 PL/SQL 编写还是很陌生。但是,据我所知,我应该能够编写一个可调用的过程,例如:

procedure create_temp_table (new_table_nme in varchar(60)
  source_table in varchar(60))
IS
    s_query varchar2(100);
BEGIN
    s_query := 'CREATE TABLE ' + new_table_nme + 'AS SELECT * FROM ' + source_table;
   execute immediate s_query;

EXCEPTION
    WHEN OTHERS THEN
       IF SQLCODE = -955 THEN
           NULL;
       ELSE
           RAISE;
       END IF;
END;

然后我会简单地调用如下过程:

create_temp_table('tmp.clients', 'live.clients');
create_temp_table('tmp.customers', 'live.customers');
    考虑到上述问题,我提出的方法是否合理? 过程调用中的数据类型是否合理,即是否应使用 varchar2(60),或者是否可以强制“source_table”参数为模式中的表名?如果表名超过 60 个字符会怎样? 我希望能够传递第三个非必需参数,以防数据必须以微不足道的方式进行限制,即处理“WHERE type_id = 1”的情况。如何修改过程以包含仅偶尔使用的参数以及如何修改其余代码。我可能会添加某种 IF/ELSE 语句来检查第三个参数是否不为 NULL,然后相应地构造 s_query。 如何检查表是否实际创建成功?

    我想捕获另外两个异常,即

    新表(例如'tmp.clients')已经存在;和 源表不存在。

    书面的例外是否处理这些情况?

    更一般地说,我可以从哪里获得 SQL 错误代码及其含义?

如果对代码提出任何改进建议,我们将不胜感激。

【问题讨论】:

只使用全局临时表。创建表的代码让我觉得原人对 SQL Server 很熟悉。 【参考方案1】:

您可以通过使用 GLOBAL 临时表来摆脱大量代码(逐渐!)。 立即执行不是一个坏习惯,但如果有其他选项,那么应该使用它们。全局临时表在您想要提取和转换数据的地方很常见,但一旦处理完毕,您就不再需要它,直到下一次加载。每个用户只能看到他们插入的数据,不会生成重做日志。如果需要,您可以index the data 进行更快的查询。

类似的东西

-- 创建表

create global temporary table GT_CLIENTS
(
  id                    NUMBER(10) not null,
  Client_id             NUMBER(10) not null,
  modified_by_id        NUMBER(10),
  transaction_id        NUMBER(10),
  local_transaction_id  VARCHAR2(30) not null,
  last_modified_date_tz TIMESTAMP(6) WITH TIME ZONE not null
)
on commit preserve rows;

我推荐在提交时保留行选项,以便您可以调试您的过程并查看表中的内容。

用法是

INSERT INTO GT_CLIENTS
SELECT * FROM live.clients;

【讨论】:

我不确定我是否遵循。我了解全局临时表可用于存储复杂查询的结果。但是,如果我所做的只是将现有表从实时生产环境复制到我自己的架构中,那么我看不到您提议的好处。 除非我没有抓住重点,否则您的解决方案需要为每个全局临时表编写大量代码,即需要为 GT_CLIENTS、GT_CUSTOMERS、GT_SUPPLIERS 等编写单独的代码。这正是我试图避免的事情。 我接受全局临时表还有其他好处(可能是内存或存储空间较低),我很高兴被说服,但目前我看不出您提出的解决方案如何纯粹是因为需要编写的代码量对我有好处。 @user2948208 如果您要告诉我们代码的重点是什么,可以提供更深入的答案。从高级视图来看,除非存在某种复杂的业务案例,否则定期删除和创建表并不是最佳实践。其他不知道意图的用户滥用的可能性也是无穷无尽的。【参考方案2】:

如果这是您希望最小化更改的路径,则源表不存在的错误是 -942,您将希望停止而不是继续,因为您的临时表尚未创建。同样,如果您收到对象已存在错误,则继续操作将是有问题的,因为您不会使用新数据重新加载它 - 创建失败,因此表仍然具有上次运行的数据。所以我肯定会多考虑一下你的异常处理程序。

也就是说,我也同意这通常不是最好的做事方式。在多用户环境中创建和删除对象是一场灾难,当有更合适的选项可用时,这似乎是一种愚蠢的资源浪费。

【讨论】:

感谢您提供代码 -942 的建议。正如我在上面评论的那样,我很高兴以“尽可能最好的方式”做事,但你并没有真正解释如何,只是说你同意前一个人。为此,我可能错过了 Global Temporary Tables 的要点,因为似乎需要为每个此类表编写大量代码。此外,这并不是一个真正的多用户环境,尽管这些表确实只需要很短的时间作为更大查询的输入 是的,您需要创建全局临时表,但是一旦创建,您只需像使用当前创建语句一样插入它们。因此,一旦它们在您的架构中,就没有显着的额外编码,并且您消除了创建/删除对象的开销,并且 GTT 不会生成重做日志或回滚信息,从而显着减少了向其中插入数据的开销。跨度>

以上是关于PL/SQL 存储过程创建表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用存储过程创建随机函数? PL/SQL

PL/SQL 存储过程填充事实表

存储过程的 PL/SQL 动态表名

oracle存储过程连续执行结果不同

在存储过程中创建动态表而在 PL/SQL 块表中创建时权限不足[重复]

Oracle 存储过程 Procedure