Oracle:PL/SQL 中查看值是不是存在的最快方法:列表、VARRAY 或临时表

Posted

技术标签:

【中文标题】Oracle:PL/SQL 中查看值是不是存在的最快方法:列表、VARRAY 或临时表【英文标题】:Oracle: Fastest Way in PL/SQL to See if Value Exists: List, VARRAY, or Temp TableOracle:PL/SQL 中查看值是否存在的最快方法:列表、VARRAY 或临时表 【发布时间】:2014-01-20 18:42:13 【问题描述】:

更新如果您想查看冗长的原始问题,请查看编辑。这是问题的更清晰的简短版本...

我需要查看GroupA(并非总是GroupA,这会更改每次循环迭代)是否存在于大约 200 个组的 [list,varray,temp 表,随便什么] 中。我如何存储这 200 个组完全由我控制。但是我想将它们存储在一个适合于最快“存在”检查的构造中,因为我必须在循环中针对不同的值检查这个列表很多次(并不总是GroupA)。那么在 PL/SQL 中什么是最快的,检查一个列表...

IF 'GroupA' IN ('GroupA','GroupB') THEN...

或使用 MEMBER OF... 检查 VARRAY

IF 'GroupA' MEMBER OF myGroups THEN

或以这种方式检查 VARRAY...

FOR i IN myGroups.FIRST .. myGroups.LAST
LOOP
    IF myGroups(i) = 'GroupA' THEN
        v_found := TRUE;
        EXIT;
    END IF;
END LOOP;

或检查关联数组... will test this tomorrow

更新:来自每个人的建议的最终测试结果 谢谢大家。 我运行了这些测试,循环了 1000 万次,使用LIKE 的逗号分隔字符串似乎是最快的,所以我想这些点必须归于@Brian McGinity(时间在下面的 cmets 中)。但由于时间如此接近,我采用哪种方法可能并不重要。我想我会使用VARRAY MEMBER OF 方法,因为我可以使用一行代码(批量收集)加载数组,而不必循环游标来构建字符串(感谢@Wernfried 将MEMBER OF 带到我的注意)...

逗号分隔列表,例如:,GroupA,GroupB,GroupC,...大约 200 个组...(通过循环光标生成的列表)

FOR i IN 1 .. 10000000 loop
    if myGroups like '%,NONE,%' then
        z:=z+1;
    end if;
end loop;
--690msec

相同的逗号分隔列表(通过循环游标生成的列表)...

FOR i IN 1 .. 10000000 loop
    if instr(myGroups, ',NONE,') > 0 then   
        z:=z+1;
    end if;
end loop;
--818msec

varray,相同的200组(批量收集制作的varray)...

FOR i IN 1 .. 10000000 loop
    IF 'NONE' MEMBER of myGroups THEN
        z:=z+1;
    end if;
end loop;
--780msec

@Yaroslav Shabalin 建议的关联数组方法(通过循环游标制作的关联数组)...

FOR i IN 1 .. 10000000 loop
    if (a_values('NONE') = 1) then
        z:=z+1;
    end if;
end loop;
--851msec

【问题讨论】:

为什么要顺序处理?当两个表之间存在连接时,为什么不将临时表批量更新为 Y。 1 对 1 处理是瓶颈所在,它只是通过在循环中进行子查询而更加复杂。 RDBM 处理数据集的效率比一项一项活动高得多。 这个必须在循环内检查的查询不能与正在循环的主查询连接,因为两者之间必须发生一个函数,并且这些函数执行其他查询获取相关查询所需的内容。它不是我创建的数据结构。我希望它可以重新设计,但它不能。我必须使用我在这个上得到的东西。 @gfrobenius,将它保存在 oracle 中而不调用函数的最快方法。如果您需要将 pgroup 作为列表并且还想知道用户是否是该组的成员,则使用 1 次查询并返回 2 列:theGroupList、isInGroup_YN @BrianMcGinity 我没有调用函数。我基本上是在问“哪种类型的存储适合最快的存在检查”?因为我可以随心所欲地存储这些组,而且我只做一次,但存在性检查会在一个循环中发生多次。 你能把它们存储在一个表中,比如:userid、group吗?每个用户 ID/组 1 行。然后像任何其他索引查询一样查询该表。这将比将它们内联存储为更快:userid, csv_grouplist 【参考方案1】:

myGroup 是一个可变数组吗?如果它是一个字符串,尝试类似:

select 1
  from dual
 where 'abc,NONE,def' like '%,NONE,%'

很难遵循您正在工作的约束...如果可能,请在 sql 中执行所有操作,这样会更快。

更新:

因此,如果您已经在 plsql 单元中并想留在 plsql 单元中,那么上面的逻辑将如下所示:

declare
    gp varchar2(200) := 'abc,def,NONE,higlmn,op';
  begin
    if ','||gp||',' like '%,NONE,%' then
      dbms_output.put_line('y');
    else
      dbms_output.put_line('n');
    end if;
  end;

如果它本身处于循环中,则将列表设为:

declare
    gp varchar2(200)  := 'abc,def,NONE,higlmn,op';
    gp2 varchar2(200) := ',' || gp || ',';
  begin
    if g2 like '%,NONE,%' then
      dbms_output.put_line('y');
    else
      dbms_output.put_line('n');
    end if;
  end;

也可以试试 instr,它可能比 like 更快:

  declare
    gp varchar2(200) := ',abc,def,NONE,hig,';
  begin
    if instr(gp, ',NONE,') > 0 then
      dbms_output.put_line('y');
    else
      dbms_output.put_line('n');
    end if;
  end;

我不知道这是否比提到的其他解决方案更快(这是一个很好的机会),这是其他尝试。

【讨论】:

我需要查看 GroupA 是否存在于 200 个左右的组的 [list,varray,temp 表, 不管] 中。我如何存储这 200 个组完全由我控制。但我想将它们存储在一个适合于最快“存在”检查的构造中,因为我将不得不在循环中针对不同的值多次检查这个列表(并不总是GroupA)。有意义吗? 我仍然需要澄清...您是否试图决定是否要将组存储在表中:userid、csv_group_member_list 其中每个用户有 1 行或在表中为:userid、single_group其中 1 个用户将有很多行(每个组 1 个) 我不想存储在表中,因为这样我就必须在每次循环迭代时查询数据库。我想将它们存储在一些 pl/sql 数据类型中,无论哪种数据类型具有最快的“存在”检查。我更新了原始问题。见上半部分。【参考方案2】:

我没有得到您的完整问题,但也许此功能可以帮助您: MEMBER Condition

WHERE 'groupA' MEMBER of myGroups 

【讨论】:

啊,这是检查 VARRAY 的一种更短的方法,我喜欢这样。所以我可以这样做...IF 'GroupA' MEMBER of myGroups THEN... 但是,如果 myGroups 包含 'GroupA','GroupB','GroupC'... 之类的数据,那是否比 IF 'GroupA' IN myGroups THEN... 更快?这就是我要问的问题。【参考方案3】:

您是否考虑过使用associative arrays,也就是以前称为“索引表”?由字符串索引的关联数组通过隐式使用值的 B*-tree 组织进行优化,以实现高效查找。它相当于其他编程语言中的 PL/SQL 哈希表。

例如,如果您将数组定义为:

type t_values is table of number index by varchar2(20);

然后将GroupA 等分配给键,将1 分配给每个相应的值:

a_values t_values;
for c_cursor in (select ...)
loop
 a_value(c_cursor.group_name) := 1;
end loop;

当您尝试访问不存在索引的值时,您将得到 null。而对于您返回的任何真实索引 1

(a_value('GroupA') = 1) => TRUE
(a_value('Some_not_existent_index') IS NULL) => TRUE

【讨论】:

我确实在其他领域使用关联数组。我没有为此考虑它,因为我认为这将是矫枉过正,因为我不需要 NAME/VALUE。但我没想到只是忽略这些价值观。明天我回到办公室后测试一下。

以上是关于Oracle:PL/SQL 中查看值是不是存在的最快方法:列表、VARRAY 或临时表的主要内容,如果未能解决你的问题,请参考以下文章

oracle怎样查询数据库函数是不是被执行

从 oracle PL/SQL 查看变量的值

预定义的 ORACLE PL/SQL 异常在哪里?

如何在 pl/sql (oracle 9i) 中查找数据类型的大小?

“如何修复 oracle pl/sql 中的触发器?

Oracle PL/SQL - 循环值作为没有动态 SQL 的动态列名