在Oracle中,IN运算符后面是否有数据类型?

时间:2019-04-08 19:26:18

标签: oracle plsql

我对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(从表(集合)中选择column_value)

因此,我认为它们可能有一些共同点,例如我不知道的数据类型。如果存在这种数据类型,查询代码可能会变得越来越简单。

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虚拟表替换时,该查询将永远运行,尽管它也不会产生任何错误。我不得不杀死它。我尝试了几次,其表现方式也一样。

有什么建议吗?

4 个答案:

答案 0 :(得分:3)

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表,因为“从表中选择COLUMN_VALUE(MB_START_TABLE)” 将始终获取本地存储的值。

答案 1 :(得分: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可能会选择实现大型结果集,这意味着要写入磁盘或从磁盘读取数据。

答案 2 :(得分:0)

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

在您的示例中,看起来mb_startdate,因此,如果您还没有一个独立的table of date集合类型,则需要定义一个独立的集合类型然后在table()member of表达式中使用。

答案 3 :(得分:0)

两种解决方案都可以,但是要付出一定的代价。

原始代码需要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不允许并且在这两种方法之间进行切换。很抱歉,谢谢大家的帮助。)