当结果可能为空时,如何在PL / SQL中选择一个变量?


75

考虑到查询可能不返回任何内容,有没有办法只运行一次查询以选择一个变量,那么在这种情况下变量应为null。

当前,我不能select into直接做一个变量,因为如果查询什么都不返回,PL / SQL会抱怨变量没有被设置。我只能运行两次查询,第一个查询执行计数,如果计数为零,则将变量设置为null,如果计数为1,则选择进入变量。

因此,代码如下所示:

v_column my_table.column%TYPE;
v_counter number;
select count(column) into v_counter from my_table where ...;
if (v_counter = 0) then
    v_column := null;
elsif (v_counter = 1) then
    select column into v_column from my_table where ...;
end if;

谢谢。

更新:我没有使用异常的原因是,在分配之后v_column,我仍然具有一些以下逻辑,并且我必须goto在异常部分中使用以跳回到以下代码。我有点犹豫goto

Answers:


128

您只需NO_DATA_FOUND将变量设置为即可处理异常NULL。这样,只需要一个查询。

    v_column my_table.column%TYPE;

BEGIN

    BEGIN
      select column into v_column from my_table where ...;
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        v_column := NULL;
    END;

    ... use v_column here
END;

@Adam,我没有使用异常的原因是,在分配之后v_column,我仍然有一些遵循的逻辑,并且我必须在异常部分中使用“ goto”跳回到以下代码。我有点犹豫goto
智人2011年

6
@Sapience,这不是问题,只需将逻辑放在此嵌套块的END之后即可。
Tony Andrews

1
@Shannon:感谢您的编辑。我自然使用匿名PL / SQL块,所以我没有意识到它是模棱两可的。
亚当·佩恩特

17

我知道这是一个老话题,但我仍然认为值得回答。

select (
        SELECT COLUMN FROM MY_TABLE WHERE ....
        ) into v_column
from dual;

使用示例:

declare v_column VARCHAR2(100);
begin
  select (SELECT TABLE_NAME FROM ALL_TABLES WHERE TABLE_NAME = 'DOES NOT EXIST')
  into v_column 
  from dual;
  DBMS_OUTPUT.PUT_LINE('v_column=' || v_column);
end;

1
是的,它是PLSQL。它工作正常。为了简单起见,我只是跳过了开始和结束内容。
jpe 2014年

@Miki:我添加了一个示例来说明它是PLSQL,它可以工作。
jpe 2014年

1
我在这里看到了这种技术:stackoverflow.com/a/42415454/577052当我将多个列写入变量时,它似乎不起作用。我得到ORA-00913太多价值。
BernhardDöbler'17

1
感谢您的评论!没错,它仅适用于1列,因为标量子查询只能返回1个值。我希望有人能够改变这一点……
jpe

我剥离了这个技巧,并创建了一个漂亮的选择查询。谢谢:(从double中选择round(round(months_between('30 -may-2018',c.column_date),0)/ 12,1)作为josh
JoshYates1980 '18

9

使用MAX怎么办?这样,如果找不到数据,则将变量设置为NULL,否则将设置为最大值。
由于您希望使用0或1值,因此可以使用MAX。

v_column my_table.column%TYPE;
select MAX(column) into v_column from my_table where ...;

不错,当查询返回多行时它也可以工作
pomber

7

使用Cursor FOR LOOP语句是我最喜欢的方法。

它比使用显式游标更安全,因为您无需记住将其关闭,因此您无法“泄漏”游标。

您不需要“放入”变量,不需要“ FETCH”,也不需要捕获和处理“ NO DATA FOUND”异常。

尝试一下,您将永不退缩。

v_column my_table.column%TYPE;

v_column := null;

FOR rMyTable IN (SELECT COLUMN FROM MY_TABLE WHERE ....) LOOP
  v_column := rMyTable.COLUMN;
  EXIT;  -- Exit the loop if you only want the first result.
END LOOP;

5

从以上所有答案中,比约恩的答案似乎是最优雅,最简短的。我个人多次使用这种方法。MAX或MIN函数将同样出色地完成工作。随后是完整的PL / SQL,仅应指定where子句。

declare v_column my_table.column%TYPE;
begin
    select MIN(column) into v_column from my_table where ...;
    DBMS_OUTPUT.PUT_LINE('v_column=' || v_column);
end;

4

我建议使用光标。游标提取始终是单行(除非您使用批量收集),并且游标不会自动引发no_data_found或too_many_rows异常;尽管您可以在打开游标属性后检查它,以确定是否有一行以及有多少行。

declare
v_column my_table.column%type;
l_count pls_integer;
cursor my_cursor is
  select count(*) from my_table where ...;

begin
  open my_cursor;
    fetch my_cursor into l_count;
  close my_cursor;

  if l_count = 1 then
    select whse_code into v_column from my_table where ...;
  else
    v_column := null;
  end if;
end;

或者,甚至更简单:

    declare
    v_column my_table.column%type;
    cursor my_cursor is
      select column from my_table where ...;

    begin
      open my_cursor;
        fetch my_cursor into v_column;
        -- Optional IF .. THEN based on FOUND or NOTFOUND
        -- Not really needed if v_column is not set
        if my_cursor%notfound then
          v_column := null;
        end if;
      close my_cursor;
    end;

感谢您的回复。但是我要避免的两次运行相同where子句的查询,第一个是判断条件是否存在,第二个是将现有记录提取到变量中。
智人

2
@Sapience:不好意思,我掩饰了你的第一句话。在这种情况下,使用cursor属性非常简单(我建议使用第二个选项)。只需在游标中写入实际查询(否COUNT(*)),打开游标并将结果提取到本地变量中即可。然后,使用该%FOUND属性确定所需的状态。这样,您只执行一次查询,并且不需要异常块并避免SELECT .. INTO潜在的异常。

2

我使用这种语法来提高灵活性和速度-

    begin
    --
    with KLUJ as
    ( select 0 ROES from dual
       union 
      select count(*) from MY_TABLE where rownum = 1
    ) select max(ROES) into has_rows from KLUJ;
    --
    end;

Dual返回1行,rownum添加0或1行,并将max()组精确地添加到1。这将为表中没有行提供0,为任何其他数目的行提供1。

我扩展了where子句以按条件对行进行计数,删除rownum以对满足条件的行进行计数,并增加rownum以对满足条件的行进行计数,直至达到限制。


-3

COALESCE将始终返回第一个非空结果。这样,您将获得所需的计数或0:

select coalesce(count(column) ,0) into v_counter from my_table where ...;

Count始终会返回一个数字,无需将其合并。但是,这个问题并不是真的要增加数量。作者只是为了避免例外而获得计数。如果删除聚合函数,由于可能没有行,合并将无助于避免出现NO_DATA_FOUND。
乔恩·海勒2013年

同样,将函数环绕在这样的列上通常会跳过可能已使用的任何索引,因为编译器无法确定函数是否可以更改列的值,从而使其无法在索引中找到,因此它根本不会查找任何索引。(您可以创建基于函数的索引来提供帮助,但实际上,谁知道这样做?)
grokster 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.