字符串连接 select 查询未给出结果 pl/sql 脚本

String concatenation select query is not giving result pl/sql script

我正在尝试动态构造查询,但在字符串连接后 select 语句未在 pl/sql 中产生任何结果。

请帮我解决这个问题

DECLARE
  person_id NUMBER;
BEGIN
  DECLARE
    age_where VARCHAR2(100 CHAR);
    TEMP_WHERE     VARCHAR2(100 CHAR) := '';
    add_temp_where BOOLEAN            := true;
    
  begin
    age_where := q'[ and age=28]';
    
    IF(ADD_TEMP_WHERE) THEN
      TEMP_WHERE := age_where;
    END IF;
    
  SELECT id INTO person_id FROM PERSON WHERE name = 'David' || TEMP_WHERE ;
  EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('no data');
  END;
  DBMS_OUTPUT.PUT_LINE('result : ' || person_id);
END;

Table 个条目

ID      NAME        AGE     ADDRESS     SALARY
-----------------------------------------------
1       David       28      PURAM       30000
2       Vimal       30      MARUR       20000

输出:

anonymous block completed
no data
result : 

为此你需要动态 SQL。

SQL> DECLARE
  2     person_id       NUMBER;
  3     age_where       VARCHAR2 (100 CHAR);
  4     TEMP_WHERE      VARCHAR2 (100 CHAR) := '';
  5     add_temp_where  BOOLEAN := TRUE;
  6     l_str           VARCHAR2 (400);
  7  BEGIN
  8     age_where := q'[ and age=28]';
  9
 10     IF (ADD_TEMP_WHERE)
 11     THEN
 12        TEMP_WHERE := age_where;
 13     END IF;
 14
 15     l_str := q'[SELECT id FROM PERSON WHERE name = 'David']' || TEMP_WHERE;
 16
 17     EXECUTE IMMEDIATE l_str
 18        INTO person_id;
 19
 20     DBMS_OUTPUT.PUT_LINE ('result : ' || person_id);
 21  END;
 22  /
result : 1

PL/SQL procedure successfully completed.

SQL>

您的 objective 是根据输入参数在查询中使用动态谓词。这是一个经常性的任务。

最重要的想法是要意识到,您应该始终使用 绑定变量 进行生产查询,即 ommit 构造,例如 WHERE name = 'David' || TEMP_WHERE.

PL/SQL有3个选项

使用 IF - ELSE

这是最简单的选项,其中输入参数的每个组合都有一个 IF/ELSE 分支

declare
 l_person_id  int;
 l_name varchar2(10) := 'David';
 l_age int := 28;
 add_temp_where int := 1; 
begin
  if add_temp_where = 0 then
    select id into l_person_id from person where name = l_name;
  else
    select id into l_person_id from person where name = l_name and age = l_age;
  end if;  
  dbms_output.put_line(l_person_id);    
end;
/

这在您的情况下可以使用一个可选参数,但不能使用更多参数。对于 3 个参数,您将需要 8 个分支。

使用 OR 禁用谓词

此选项仅使用一个 static 语句并使用 OR 逻辑来禁用不需要的参数。

请注意,我使用参数 add_temp_whereint 0,1 以便能够在 SQL.

中使用它
declare
 l_person_id  int;
 l_name varchar2(10) := 'David';
 l_age int := 28;
 add_temp_where int := 1; 
begin
  select id into l_person_id from person where name = l_name and (add_temp_where = 0 or age = l_age);
  dbms_output.put_line(l_person_id);    
end;
/

所以基本上 if add_temp_where = 0 比谓词 age = l_age 由于快捷评估而被忽略。

如果对于所有选项查询returns相似的行数(技术上使用相同的执行计划),此选项工作正常。

严重失败 以防一个选项返回整个 table 而其他只有一小部分(通过索引)。

在这种情况下,您需要使用动态 SQL。

动态SQL

您对 OR 使用了与上面相同的 技巧 ,因此您生成了以下 SQL 个字符串

-- for  add_temp_where = 1
select id  from person where name = :1 and age = :2
-- for  add_temp_where = 0
select id  from person where name = :1 and (0=0 or age = :2)'

请注意,这两个语句都有两个绑定变量,因此它们可以在一个 EXECUTE IMMEDIATE 中使用,如下所示:

declare
 l_person_id  int;
 l_name varchar2(10) := 'David';
 l_age int := 28;
 add_temp_where BOOLEAN := true;
 --
 l_sql1 varchar2(1000) := 'select id  from person where name = :1 and age = :2';
 l_sql2 varchar2(1000) := 'select id  from person where name = :1 and (0=0 or age = :2)'; 
begin
  execute immediate l_sql1 into l_person_id using l_name, l_age;
  dbms_output.put_line(l_person_id); 
  execute immediate l_sql2 into l_person_id using l_name, l_age;
  dbms_output.put_line(l_person_id);   
end;
/

两条语句是独立的,因此可以有不同的执行计划,解决了第二个选项的问题。

此选项的更多信息和学分 and here