【问题标题】:In Oracle, is there a datatype for what follows the IN operator?在 Oracle 中,IN 运算符后面是否有数据类型?
【发布时间】:2019-08-30 00:26:45
【问题描述】:

我有一个关于 Oracle PL/SQL 的问题。

在一个过程中,同一个嵌入的 SELECT 语句在查询中与 WHERE 子句一起重复使用:

...
where start_year in (
   SELECT MB_START FROM MEMBERS
)
...

过程中嵌入在 WHERE 子句中的实际 SELECT 语句是 更详细,并替换为 'SELECT MB_START FROM MEMBERS' 来制作 我的问题在这里更容易理解。这正是我要问的原因 问题:

在许多 WHERE 子句中重复相同的子查询很麻烦,使得 阻塞的代码难以阅读,并且是服务器的潜在浪费 资源。有没有办法将嵌入式子查询返回的内容保存在变量中,例如:

DECLARE
   start_years <type_for_IN_operator>;
BEGIN
   ...
   select mb_start into start_years from members;
   ...
   WHERE start_year in (start_years)
   ...
END;
/

我苦苦搜索,但未能确定 Oracle 是否支持将值保存在变量中以供 IN 运算符使用。如果支持,type_for_IN_operator 的数据类型是什么?

对答案和进一步问题的回应:

感谢大家对问题的回答。

我问这个问题是因为我观察到 IN 运算符接受一组不同形式的值,例如

  • 文字:IN(2015、2016、2017)
  • 从表中选择的列:IN(从成员中选择 mb_start)
  • 通过 SELECT 语句的集合:in (select column_value from table(collection))

因此我认为它们可能有一些共同点,例如我不知道的数据类型。如果有这样的数据类型,查询代码会简单很多。

我被APC的想法所吸引,特别是它是纯sql,虚拟表是可重用的。我试过了,但是出了点问题。

脚本原本是:

select t.col1, t.col2, ... from
(
  select ...
  from ...
  where start_year in (<that subquery>)
  union
  select ...
  from ...
  where start_year in (<that subquery>)
  union
  ...
) t
join ...
...

现在使用虚拟表并替换第一个原始子查询:

with sqf as (
    SELECT MB_START FROM MEMBERS
)
select t.col1, t.col2, ... from
(
  select ...
  from ...
  where start_year in (select * from sqf)
  union
  select ...
  from ...
  where start_year in (<that subquery>)
  union
  ...
) t
join ...
...

碰巧查询完成的速度甚至比原始代码还要快。但是,当第二个 也被替换为 sqf 虚拟表时,查询将永远运行,尽管它也没有出现任何错误。我不得不杀了它。我又试了几次,结果还是一样。

有什么建议吗?

【问题讨论】:

    标签: oracle plsql


    【解决方案1】:

    oracle 中的任何查询都会根据选择的列返回一个游标。是否在 IN 条件下调用它并不重要。

    但是,如果您想保存子查询的结果以便在多个查询中重复使用,您可以创建一个 TYPE 并在其中批量收集您的值。

    CREATE TYPE MEMBER_TAB_TYPE AS TABLE OF DATE;
    
    DECLARE 
      MB_START_TABLE MEMBER_TAB_TYPE;
    BEGIN 
      SELECT MB_START BULK COLLECT INTO MB_START_TABLE FROM MEMBERS;
      ....
      WHERE START_YEAR IN (SELECT COLUMN_VALUE FROM TABLE(MB_START_TABLE));
      ....
    END;
    

    您可以在程序中任意多次使用 MB_START_TABLE,而无需实际查询 MEMBERS 表,因为 "SELECT COLUMN_VALUE FROM TABLE(MB_START_TABLE)" 将始终获取本地存储的值。 p>

    【讨论】:

    • 如果第一个查询的结果集很小,这是一个不错的解决方案。然而,对于大型结果集,它会带走许多查询优化机会,因为代码会强制 Oracle 实现中间结果。所以保存代码可能是有代价的。
    • 同意。但是,假设是由于他使用的是 IN 条件下的数据集,因此数据集不会很大,内存占用会很小。
    • 这一切都是正确的,但忽略了一点,即自 Oracle 9i 以来,Oracle 已经提供了一种在纯 SQL 中解决此问题的方法:WITH 子句。
    【解决方案2】:

    有没有办法将嵌入的子查询返回的内容保存在变量中

    有点。它是 WITH 子句,也称为子查询分解。自 9i 以来,它一直是 Oracle SQL 功能的一部分。

    使用您发布的示例:

    with sqf as (
        SELECT MB_START FROM MEMBERS
    )
    select * from your_table
    where col1 in ( select * from sqf )
    and col2 in  ( select * from sqf )
    and col3 not in ( select * from sqf )
    /
    

    WITH 子句执行一次,并在后续引用中重复使用。我们可以在FROM子句中使用,包括其他子查询因素的FROM子句。

    它的明显优点在于它是纯 SQL,因此不需要 PL/SQL、变量或类型声明。有一个陷阱,Oracle 可能会选择具体化大型结果集,这意味着写入和读取磁盘。

    【讨论】:

      【解决方案3】:

      IN 在逻辑上是一系列= 表达式,它们之间有OR,因此实际上您是在要求相等性测试的数据类型。

      在您的示例中,mb_start 看起来是 date,因此如果您还没有独立的 table of date 集合类型,则需要定义它,然后您可以在 @987654327 中使用它@ 或 member of 表达式。

      【讨论】:

      • WITH子句怎么样?
      【解决方案4】:

      两种解决方案都有效,但需要付出一定的代价。

      原始代码完成查询需要 23 秒。分解子查询需要 40 秒。 52 秒用于使用集合(从表(集合)中选择列值)。另外,必须对原始代码进行一些修改才能使这两种替代方案正常工作。

      原始查询工作正常,这是它的框架:

      select col1, col2 from
      (
         SELECT x.col_a, x.col_b, ... from ...
         where
         (
            ( conditions A AND condition B)
            OR
            ( condition C AND condition D AND condition E)
         )
         AND
         condition F
         UNION
         select B
         UNION
         select C
      ) x
      JOIN ...
      JOIN ...
      ...
      

      但因式子查询和集合替代项都被阻塞,永远运行而不会产生结果,除非第一个 UNION 运算符之前的 SELECT 被分成两个,所以还有一个 UNION:

      select col1, col2 from
      (
         SELECT x.col_a, x.col_b, ... from ...
         where
            conditions A AND condition B AND condition F
         UNION
         SELECT col_a, col_b, ... from ...
         where
            condition C AND condition D AND condition E AND condition F
         UNION
         select B
         UNION
         select C
      ) x
      JOIN ...
      JOIN ...
      ...
      

      因此,至少在我的情况下,在旧代码和新替代代码之间,需要在性能和更简洁的代码之间进行权衡。

      (我尝试单击复选标记将两个备选方案标记为解决方案,但 stackoverflow 不允许并在两者之间切换。对此表示抱歉,感谢大家的帮助。)

      【讨论】:

        猜你喜欢
        • 2014-08-20
        • 2014-06-20
        • 2011-04-03
        • 2015-03-27
        • 1970-01-01
        • 2022-06-29
        • 2023-03-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多