Oracle 11 PL SQL使用Execute Immediate将值赋给变量

时间:2017-12-11 04:57:56

标签: oracle plsql

我是PL SQL的新手并且还在学习,但我需要解决一个问题,而且我对PL SQL的了解不够快,无法快速解决我的问题。

我引用了两个表:用户和属性。我有一个过程需要3个参数: attrib_id uid attrib_value

我首先使用 attrib_id 查询属性表,以返回属性名称并将其分配给变量。我的代码到目前为止。

接下来,我想在另一个select语句中使用从前一个select语句创建的变量来查询users表,并返回与该变量所代表的属性关联的当前值。

CODE:

PROCEDURE value_update_proc_z(attrib_id INTEGER, uid IN VARCHAR2, attrib_value IN VARCHAR2)

IS
    v_old_attrib_name attributes.attribute_name%TYPE;
    v_oldattrib_value varchar2(100);
    v_mymsg varchar2(2000);

BEGIN
    EXECUTE IMMEDIATE 'SELECT attrib_name FROM attributes WHERE indx = ''' || attrib_id || '''' INTO v_old_attrib_name;

    EXECUTE IMMEDIATE 'SELECT' || v_old_attrib_name || 'FROM USERS WHERE USERID = ''' || uid || '''' INTO v_oldattrib_value;

    v_mymsg := v_old_attrib_name || ' ' || v_oldattrib_value;

END value_update_proc_z;

第一个查询应该根据传递给过程的数字从属性表中返回一个值。例如,如果 attrib_id = 1,则查询将返回first_name,如果 attrib_id = 2,则返回last_name,如果 attrib_id = 3,将被退回的电子邮件。返回的值将分配给变量 v_old_attrib_name

在我的select语句中使用变量 v_old_attrib_name ,我希望第二个查询会返回一个值,例如; last_name将返回williams,否则电子邮件将返回bob@somewhere.com。此查询的结果将分配给变量 v_oldattrib_value

目前,第一个Execute Immediate工作,当我显示消息时,我可以看到该变量的值,但是当我添加第二个Execute Immediate时,我收到一条消息,说明操作无法完成。这不是系统生成的错误,它是由前一位开发人员设置的消息。

我愿意接受改进建议。

谢谢!

4 个答案:

答案 0 :(得分:5)

动态SQL很难,因为它将编译错误转变为运行时错误。令人惊讶的是,使用动态SQL在这里发布的问题是一个简单的拼写错误,如果该语句是作为静态SQL编写的话很容易发现。这似乎就是这种情况。

这里的标准建议是首先以静态形式编写SQL,因此您知道代码可以正常工作。然后将其转换为模板SQL以进行动态执行,注意空格,名称等。您的第二个语句是在连接的v_old_attrib_name变量的任一侧模板SQL中缺少空格。

另外,如果静态SQL有效,请不要使用动态SQL。例如,你的第一个陈述可以 - 而且应该是 - 静态。

PROCEDURE value_update_proc_z(
    attrib_id INTEGER, 
    uid IN VARCHAR2, 
    attrib_value IN VARCHAR2)

IS
    v_old_attrib_name attributes.attribute_name%TYPE;
    v_oldattrib_value varchar2(100);
    v_mymsg varchar2(2000);

BEGIN
    SELECT attrib_name 
    INTO v_old_attrib_name
    FROM attributes 
    WHERE indx = attrib_id  ;

    EXECUTE IMMEDIATE 
        'SELECT ' || v_old_attrib_name || ' FROM USERS WHERE USERID = :1' 
        INTO v_oldattrib_value
        using uid;

    v_mymsg := v_old_attrib_name || ' ' || v_oldattrib_value;

   dbms_output.put_line(v_mymsg);

END value_update_proc_z;

在你的问题中注意到这一行。

  

“这不是系统生成的错误,而是由前任开发人员设置的消息。”

但是看起来这个以前的开发者是坏代码王子。他们不仅用可怕的EAV implementation给你带来了麻烦,而且他们也在压制错误信息。诸如操作无法完成的通用消息对任何人都不利,尤其是我们。您需要知道实际的PL / SQL错误消息,以便了解程序失败的原因,这是解决问题的关键。

现在,设计良好的系统将具有某种形式的日志记录,可以显示和/或存储真正的SQLERRM。您似乎并没有在设计良好的系统上工作,但是您是否有任何错误记录?

答案 1 :(得分:2)

在第二个陈述中

EXECUTE IMMEDIATE 'SELECT' || v_old_attrib_name || 'FROM USERS WHERE USERID ...

在变量space之前和之后应至少存在一个v_old_attrib_name,如下所示:

EXECUTE IMMEDIATE 'SELECT ' || v_old_attrib_name || ' FROM USERS WHERE USERID ...

答案 2 :(得分:0)

请在下面找到工作示例。让我知道你不明白...

问题1:我是在获取attrib_value但是使用v_old_attrib_name attributes.attribute_name%TYPE; [Attribute_Name]声明了变量。应该是v_old_attrib_name attributes.attrib_name%TYPE;

问题2:如上面另一个答案中所述,您在查询中缺少一些空格。

问题3:您没有 out_variable ,IDK您查看返回值的计划是什么。

create table attributes (attrib_name varchar2(20), indx number(2));

insert into attributes values('LAST_NAME',1)

create table USERS (userid number(2), last_Name varchar2(100))

insert into users values (12,'Williams')

<强>步骤:

create or replace PROCEDURE value_update_proc_z(attrib_id INTEGER, uid IN VARCHAR2, attrib_value OUT VARCHAR2)
IS
    v_old_attrib_name attributes.attrib_name%TYPE;
    v_oldattrib_value varchar2(100);

BEGIN
    EXECUTE IMMEDIATE 'SELECT attrib_name FROM attributes WHERE indx = ''' || attrib_id || '''' INTO v_old_attrib_name;

    EXECUTE IMMEDIATE 'SELECT ' || v_old_attrib_name || ' FROM USERS WHERE USERID = ''' || uid || '''' INTO v_oldattrib_value;

    attrib_value := v_old_attrib_name || ' ' || v_oldattrib_value;

END value_update_proc_z;

执行:请注意,我们正在打印出一个输出变量

DECLARE 
  ATTRIB_ID NUMBER;
  "UID" VARCHAR2(32767);
  ATTRIB_VALUE VARCHAR2(32767);

BEGIN 
  ATTRIB_ID := 1;
  "UID" := 12;
  ATTRIB_VALUE := NULL;

  VALUE_UPDATE_PROC_Z ( ATTRIB_ID, "UID", ATTRIB_VALUE );

  DBMS_OUTPUT.Put_Line('ATTRIB_VALUE = ' || ATTRIB_VALUE);

  DBMS_OUTPUT.Put_Line('');

  COMMIT; 
END; 

答案 3 :(得分:0)

我根据您的架构中的要求创建了Sample表。我发现您必须在代码中进行3项更改才能正常工作。

1。) v_old_attrib_name attributes.attribute_name%TYPE 已更改为 v_old_attrib_name attributes.attrib_name%TYPE(根据开始块中的代码)。

我说的是上面的更改,因为在开始块中,当您在属性表上编写选择查询时,select子句中的列是“attrib_name”而不是“attribute_name”。

2.。)当您使用动态SQL时,您无法直接将列或table_name替换为变量,因为oracle在运行时需要它们来执行查询。为避免这种情况,您必须添加另一个变量,在该变量中您将形成动态查询,然后在执行立即中使用它。您将在下面的代码中看到。

3。)当您编写动态SQL时,请确保提供适当的空格,以便不会将任何内容相互连接。您也可以在下面的代码中看到。

以下代码:

PROCEDURE value_update_proc_z(attrib_id INTEGER, uid IN VARCHAR2, attrib_value IN VARCHAR2)
is
v_old_attrib_name attributes.attrib_name%TYPE;
    v_oldattrib_value varchar2(100);
    v_mymsg varchar2(2000);
   v_sql varchar2(4000);
BEGIN
    EXECUTE IMMEDIATE 'SELECT attrib_name FROM attributes WHERE indx = ''' || attrib_id || '''' INTO v_old_attrib_name;

    v_sql:='SELECT ' || v_old_attrib_name || ' FROM USERS WHERE USERID = ''' || uid || '''' ;

    EXECUTE IMMEDIATE v_sql INTO v_oldattrib_value;

    v_mymsg := v_old_attrib_name || ' ' || v_oldattrib_value;

END value_update_proc_z;

希望这能解决您的问题。

由于 ANKIT。