【问题标题】:How can you rename variables in an Oracle stored procedure?如何重命名 Oracle 存储过程中的变量?
【发布时间】:2019-05-13 17:15:53
【问题描述】:

我需要一种方法来重命名大量 Oracle 存储过程中的变量。我看到的问题是存储过程变量名称也是表中的列名称,因此编辑、替换不起作用。

例如,在以下过程中,如何将名为:COMPANYID 的变量替换为 PARAM_COMPANYID?

这样做的重点是,我们工作的地方不提供调用存储过程的能力。我们确实有能力运行 SQL 脚本。目前,当我们运行存储过程时,我们必须手动编辑将所有变量替换为实际值。

CREATE OR REPLACE PROCEDURE          SP_TEST(
, COMPANYID IN integer default null
, OUTPUT OUT types.cursor_type  
) AS


BEGIN

OPEN OUTPUT FOR

SELECT   
  COMPANYID
  FIRSTNAME,
  LASTNAME,    

from
(
    select distinct 
    COMPANYID
    FIRSTNAME,
    LASTNAME
    FROM ALL_COMPANYS C
    WHERE C.COMPANYID=COMPANYID

) TEMP
where TEMP.COMPANYID = DECODE(COMPANY,0,COMPANYID,NVL(COMPANY,COMPANYID))  

GROUP BY 
  COMPANYID
  FIRSTNAME,
  LASTNAME;

最终的结果是这样的

CREATE OR REPLACE PROCEDURE          SP_TEST(
, PARAM_COMPANYID IN integer default null
, OUTPUT OUT types.cursor_type  
) AS


BEGIN

OPEN OUTPUT FOR

SELECT   
  COMPANYID
  FIRSTNAME,
  LASTNAME,    

from
(
    select distinct 
    COMPANYID
    FIRSTNAME,
    LASTNAME
    FROM ALL_COMPANYS C
    WHERE C.COMPANYID=PARAM_COMPANYID

) TEMP
where TEMP.COMPANYID = DECODE(COMPANY,0,COMPANYID,NVL(COMPANY,PARAM_COMPANYID))  

GROUP BY 
  COMPANYID
  FIRSTNAME,
  LASTNAME;

如果有办法将所有 SQL 参数重命名为最终结果(上图),那么我可以使用 C# 编写一个编辑/替换过程,将这些存储过程转换为可运行的内联脚本。

【问题讨论】:

  • “存储过程变量名也是表中的列名” 是非常糟糕的做法。这种冲突不可避免地会导致结果集出现问题。原始脚本来自哪里?无论如何,我可以理解您为什么要编辑存储过程,但我不明白为什么存储过程的第二个版本绕过了对“调用存储过程的能力”的禁令跨度>
  • 说错的时候纠正一下,但是看DECODE函数的参数,连Oracle都分不清COMPANYID是列还是参数?!
  • @Yosh,在解码中,由于范围规则,Oracle 将从查询中获取 companyid。 @Robbieone,您可以将要引用同名过程变量的任何companyid 值更改为sp_test.companyid,但是没有算法能够为您决定哪些引用需要更改。
  • @Sentinel 是的,我也是这么想的,但是在“结果”版本中,其中一个是参数,所以初始版本永远不会产生预期的结果......谢谢.
  • 我同意 APC。这不是一个好的做法,但我发现我必须处理一些事情。带有 DECODE 语句的行取自我们的一个真实生产过程。我也想知道甲骨文如何知道哪个是哪个。不知何故,Oracle 能够分辨出差异,并且 Oracle 能够以某种方式使用自己的内部算法解析这些 proc。我希望有一种方法可以访问它,但似乎没有。谢谢 APC、Yosh、Sentinel

标签: c# oracle stored-procedures plsql


【解决方案1】:

这样的代码是不好的做法:

WHERE C.COMPANYID=COMPANYID

很明显,编写它的开发人员认为,COMPANYID 在没有别名和前缀的情况下显然指的是同名参数。实际上,编译器会应用范围内最近的匹配标签,即表列名。这样 WHERE 子句就被解释为

WHERE C.COMPANYID=C.COMPANYID

where 1=1。不好。因此,将参数重命名为独特的名称至关重要。

坏消息是,没有神奇的方法可以做到这一点。您必须手动编辑每个过程的源代码。不要考虑在全局搜索和替换操作中使用正则表达式的可能性。从您在此处发布的示例中,列引用并不总是具有别名,因此无法编写搜索和替换表达式来告诉(例如)GROUP BY COMPANYID 中的 COMPANYID 是列而不是参数。

不幸的是,您需要进行一个冗长而乏味的练习,即查找参数名称的每一次出现,确定它是否实际上是一个参数,并在适当的时候对其进行修改。尽量不要花太多时间希望对编写原始脚本的开发人员进行创造性的折磨。虽然可能很有趣,但它会分散注意力,并会导致引入新错误或丢失现有错误。

这是一个脚本,您可以使用它来确定未来任务的范围(假设您有一个编译这些过程的架构)。

select package_name
      , object_name as procedure_name
      , argument_name as parameter_name
      , case when length(argument_name) > 28 then '!' end as uh_oh
from user_arguments
where position > 0 -- eliminate function return values
and argument_name in ( select column_name from user_tab_columns )
order by package_name
         , object_name
         , position

user_tab_columns 上的子查询是可选的。它允许您仅处理现在很麻烦的参数。如果要处理所有参数,请忽略它。

uh_oh 属性将识别过长而无法使用简单的P_ 前缀重命名的参数。您建议的代码建议将 PARAM_ 作为前缀,但它不必要地长,并且可能比两个字符的前缀产生更多的名称更改。

【讨论】:

  • 谢谢@APC。这是一篇很棒的文章。我仍然在想也许有人已经创建了一个解析器(在某个地方)来做到这一点。如果我找不到一个,那么我可以尝试自己创建一个。为了回答您的问题,这绕过了调用 proc 的 sp 禁令的原因是因为我可以创建 c# 代码来提取 proc 的文本,并用 VALUES 替换所有参数实例。整个脚本将作为单个语句运行,而不是作为 proc 运行。
  • 如果您认真考虑使用 PL/SQL 解析器来完成这项任务,请查看 Porcelli。 Find it here。不暗示或提供任何保证。
  • 这更符合我正在寻找的内容。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-14
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多