【问题标题】:How do I check for a IN condition against a dynamic list in Oracle?如何根据 Oracle 中的动态列表检查 IN 条件?
【发布时间】:2011-03-28 13:57:44
【问题描述】:

编辑:更改标题以适合下面的代码。

我正在尝试从 Oracle 表中检索可接受值的列表,然后在将某些字段与所述列表进行比较时对另一个执行 SELECT。

我试图用光标(如下所示)执行此操作,但失败了。

DECLARE
    TYPE gcur IS REF CURSOR;
    TYPE list_record IS TABLE OF my_table.my_field%TYPE;
    c_GENERIC gcur;
    c_LIST list_record;
BEGIN
    OPEN c_GENERIC FOR
    SELECT my_field FROM my_table
    WHERE some_field = some_value;

    FETCH c_GENERIC BULK COLLECT INTO c_LIST;

    -- try to check against list
    SELECT * FROM some_other_table
    WHERE some_critical_field IN c_LIST;

END

基本上,我要做的是将可接受的值列表缓存到一个变量中,因为我稍后会反复检查它。

您如何在 Oracle 中执行此操作?

【问题讨论】:

  • 令人讨厌的是,如果您的最终操作是 UPDATE 或 DELETE,您的代码将完美运行 - 只有 SELECT 不接受批量绑定作为输入。
  • 顺便说一句,你有资源泄漏 - 没有关闭参考光标。为什么不是好旧的本地光标?

标签: oracle list plsql cursor


【解决方案1】:

我们可以使用集合来存储值以满足您的目的,但它们需要声明为 SQL 类型:

create type list_record is table of varchar2(128)
/

这是因为我们不能在 SQL 语句中使用 PL/SQL 类型。唉,这意味着我们不能使用 %TYPE%ROWTYPE,因为它们是 PL/SQL 关键字。

您的程序将如下所示:

DECLARE
    c_LIST list_record;
BEGIN

    SELECT my_field 
    BULK COLLECT INTO c_LIST 
    FROM my_table
    WHERE some_field = some_value;

    -- try to check against list
    SELECT * FROM some_other_table
    WHERE some_critical_field IN ( select * from table (c_LIST);

END;    

“我看到你仍然必须执行一个 SELECT 语句填充列表 对于 IN 子句。”

如果值在表中,则没有其他方法可以将它们放入变量中:)

“我认为有一个 显着的性能增益使用 this over a direct semi-join"

不一定。如果您只使用一次值,那么子查询肯定是更好的方法。但是,由于您想在多个离散查询中使用相同的值,因此填充集合是更有效的方法。

在 11g 企业版中,我们可以选择使用 result set caching。这是一个更好的解决方案,但并不适合所有表格。

【讨论】:

  • 谢谢。我明白你在说什么,但我看到你仍然必须执行一个 SELECT 语句来填充 IN 子句的列表。我认为使用它比直接半连接有显着的性能提升(如下面的 Adam Musch 提供的)? :)
  • 谢谢。 list_record 这个名字确实有点让人困惑,因为它确实是一张桌子......
  • @UlrikLarsen - 但它不是表格,而是类型。类型不存储数据,但用于定义存储数据的结构(持久或会话绑定)。虽然如果它是我的代码,我会给它一个更好的名称 - 甚至使用 Oracle 内置类型,例如 sys.dbms_debug_vc2coll。但我保留了 Seeker 的原始名称,以便他们更清楚地了解我为使他们的代码正常工作所做的更改。
  • 是的,它不是一个表,它是一种“是...的表”的类型。因此,它不应该命名为 _record,而应该命名为 _table,同意吗?
【解决方案2】:

为什么要拉列表而不是使用半连接?

SELECT * 
  FROM some_other_table 
 WHERE some_critical_field IN (SELECT my_field 
                                FROM my_table
                               WHERE some_field = some_value); 

【讨论】:

  • 因为主 SELECT 语句可能会重复执行。我在想,如果迭代次数真的很高,任何嵌套的 SELECT 都会效率低下,所以我试图缓存我的列表。
  • 听起来并不讽刺,但听起来你认为你有性能优化问题,而不是你知道你有一个问题。我建议对我提供的解决方案与 APC 的解决方案进行基准测试,看看是否有任何显着差异。
  • 其实,我知道我有一个优化问题。我现在正忙于重构别人的代码,因为一个由块组成的 SP 就像我在上面伪输入的代码一样,正在与我们的大型数据集抗衡。 :) 说实话,我真的很想对您的两种解决方案进行基准测试以满足我的好奇心,但这只是我宁愿尝试一些新的东西来最终解决这个问题。 :D
  • 如果嵌套子查询不引用主查询中的任何内容,Oracle 应该确定子查询可以执行一次,然后重新使用结果集。这应该与将 C_LIST 作为变量/对象集合类型相同。我们都知道优化器通常会决定做最坏的事情。提示可能会有所帮助,或者以其他方式强制执行不同的执行计划。
猜你喜欢
  • 2021-07-11
  • 2020-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多