select中的子查询-'IN'子句中的非分组值

时间:2018-08-03 11:11:02

标签: sql oracle oracle11g

假设以下简化模式:

create table main_table
(
    a number,
    b number,
    c number
);

create table other_table
(
    c number,
    d number
)

现在,我要实现的目标: 我对main_table有一个查询,该查询按a,b分组。 我需要在select子句的子查询中使用“ c的所有值”,以从其他表中获取一些数据。 不幸的是,我无法加入另一个桌子。

伪代码为:

select mt.a,
       mt.b,
       (select /* some aggregated value */
          from other_table ot
         where ot.c in (all_values_of_c_within_group)
       )
  from main table mt
 group by mt.a, mt.b

我知道有两种方法可以处理此问题:

  1. 在other_table上使用join,然后从那里聚集值-不幸的是,由于实际查询的结构(3个嵌套视图,800 sloc,30个值的分组方式-长话),我无法做到这一点
  2. 使用listagg,然后使用“ instr”将其“ delistagg”。伪代码:

/*(...)*/
(select /* some_aggregated_value */
   from other_table ot
  where instr(',' || listagg(
                     to_char(mt.c), ',') within group (order by 1),
              ',' || ot.c) > 0
)
/*(...)*/

但这只是糟糕的代码,它会自动阻止在other_table.c上使用任何潜在的现有索引。

是否有一种语法可以正确获取“组内所有列的值?

3 个答案:

答案 0 :(得分:1)

没有一些数据和预期结果尚不清楚您要实现的目标,但我认为您可以使用集合来完成您想做的事情:

SQL Fiddle

Oracle 11g R2架构设置

create table main_table( a, b, c ) AS
  SELECT 1, 1, 1 FROM DUAL UNION ALL
  SELECT 1, 1, 2 FROM DUAL UNION ALL
  SELECT 1, 1, 3 FROM DUAL
/

create table other_table( c, d ) AS
  SELECT 1, 4 FROM DUAL UNION ALL
  SELECT 3, 6 FROM DUAL UNION ALL
  SELECT 5, 8 FROM DUAL
/

CREATE TYPE number_table AS TABLE OF NUMBER
/

查询1

SELECT a,
       b,
       ( SELECT LISTAGG( d, ',' ) WITHIN GROUP ( ORDER BY d )
         FROM   other_table
         WHERE  c MEMBER OF m.cs
       ) ds
FROM   (
  SELECT a,
         b,
         CAST( COLLECT( c ) AS number_table ) AS cs
  FROM   main_table
  GROUP BY a, b
) m

Results

| A | B |  DS |
|---|---|-----|
| 1 | 1 | 4,6 |

查询2 :但是使用LEFT OUTER JOIN似乎更简单:

SELECT a,
       b,
       LISTAGG( d, ',' ) WITHIN GROUP ( ORDER BY d ) ds
FROM   main_table m
       LEFT OUTER JOIN other_table o
       ON ( m.c = o.c )
GROUP BY a, b

Results

| A | B |  DS |
|---|---|-----|
| 1 | 1 | 4,6 |

答案 1 :(得分:1)

您也许可以汇总子查询,例如以sum作为聚合函数:

select mt.a,
       mt.b,
       sum(
         (select d
            from other_table ot
           where ot.c = mt.c)
       ) as sum_d
  from main_table mt
 group by mt.a, mt.b;

包含一些虚构数据:

insert into main_table values (1, 2, 3);
insert into main_table values (1, 2, 4);
insert into main_table values (2, 3, 4);
insert into main_table values (2, 3, 5);
insert into main_table values (2, 3, 6);

insert into other_table values (3, 10);
insert into other_table values (4, 11);
insert into other_table values (5, 12);
insert into other_table values (6, 13);
该查询给出的

         A          B      SUM_D
---------- ---------- ----------
         2          3         36
         1          2         21

正如您所指出的,还有一行:

insert into main_table values (2, 3, 4);

该查询多次计算匹配的c的{​​{1}}值,因此您得到的是47而不是36:

d

您可以添加 A B SUM_D ---------- ---------- ---------- 2 3 47 1 2 21

distinct

这假设select mt.a, mt.b, sum(distinct (select d from other_table ot where ot.c = mt.c) ) as sum_d from main_table mt group by mt.a, mt.b; A B SUM_D ---------- ---------- ---------- 1 2 21 2 3 36 或至少c的组合在c, d中是唯一的。

答案 2 :(得分:0)

这应该可行,并且不应该像Alex的回答那样对other_table施加唯一性要求。

select mt.a,
       mt.b,
       (select sum(d) /* some aggregated value */
          from other_table ot
         where ot.c in ( SELECT mt2.c 
                         FROM main_table mt2 
                         WHERE mt2.a = mt.a AND mt2.b = mt.b
                       ) 
       ) agg
  from main_table mt
 group by mt.a, mt.b;

每个组必须再次转到main_table,但是考虑到您已经在访问这些记录,我们应该讨论的是额外的逻辑I / O,而不是额外的物理I / O。

使用Alex Poole的测试数据(带有重复的MAIN_TABLE行),我在12c中得到了这一点:

+---+---+-----+
| A | B | AGG |
+---+---+-----+
| 2 | 3 |  36 |
| 1 | 2 |  21 |
+---+---+-----+