子查询可提高性能和可维护性

时间:2020-03-03 08:58:49

标签: sql oracle

我有这样的查询:

select Table1.column1 AS CODE, COUNT(DINSTINCT(Table2.column1 || '|' || Table2.column2)) AS COUNT
FROM   Table2
INNER JOIN Table3 ON Table3.referenceColumn = Table2.referenceColumn
INNER JOIN Table1 ON Table1.referenceColumn = Table2.referenceColumn
WHERE
....
AND Table1.column1 <> ''

查询的输出将是这样的,基本上是一个CODE和相应的COUNT,例如:

CODE    COUNT
ref002   3
ref003   1

在第一个查询之后,我有一个foreach,它将对上面的查询结果进行迭代。在foreach内,针对每个查询结果 上面,我想要获取一些Table3中可用的信息,基本上,我想要每个CODE(Table1.Column1)的Table3.column1中的所有值。因此,在foreach内,我还有另一个查询来获取每个迭代结果的Table3.column:

select Table3.column1
FROM   Table3
INNER JOIN Table3 ON Table3.referenceColumn = Table2.referenceColumn
INNER JOIN Table1 ON Table1.referenceColumn = Table2.referenceColumn
WHERE .... 
AND Table1.column1 = (equal to a parameter (Table1.column1) that is available in each foreach iteration)

这样,我可以获取第一个查询的每个Table1.column1(每个CODE)的所有Table3.column1值。

怀疑

两个查询几乎相同。 foreach之前的查询和foreach内部的查询之间的唯一区别是在SELECT部分和WHERE中,基本上是其中仅具有附加条件的地方。因此,这两个查询在性能和可维护性方面都不太好,因为两个查询几乎相同。因此,应该有可能在第一个查询中获得所有必要的信息,而不是在foreach内进行第二个查询。

您知道在第一个查询中需要进行哪些更改,除了返回CODE和COUNT之外,还为每个CODE返回Table3.column1中的所有值?这样就可以删除foreach内部的查询,并获得仅一个查询(第一个查询)所需的所有内容?第一个查询的必要输出应类似于:

CODE     COUNT     IDNUMBERS
ref002    3         ab00, cd00
ref003    1         ef00

也许select子句中的子查询可以解决此问题,但是我不了解如何为此正确使用子查询。是否有必要使用第一个查询并将第二个完整查询作为子查询放入select子句中?喜欢:

select 
Table1.column1 AS CODE, 
COUNT(DINSTINCT(Table2.column1 || '|' || Table2.column2)) AS COUNT, 
(select Table3.column1 
FROM Table3 INNER JOIN Table3 ON Table3.referenceColumn = Table2.referenceColumn 
INNER JOIN Table1 ON Table1.referenceColumn = Table2.referenceColumn 
WHERE .... 
AND Table3.column1 = Table1.column1) AS IDNUMBERS 

FROM Table2 
INNER JOIN Table3 ON Table3.referenceColumn = Table2.referenceColumn 
INNER JOIN Table1 ON Table1.referenceColumn = Table2.referenceColumn 
WHERE .... 

1 个答案:

答案 0 :(得分:1)

您可能正在寻找the collect() function,它为您提供了一个嵌套表作为主要结果集的一部分。

下面是使用默认HR模式数据的示例:

select d.department_id,
  count(e.employee_id) as cnt,
  cast(collect(e.first_name) as sys.odcivarchar2list) as names
from departments d
left join employees e on e.department_id = d.department_id
group by d.department_id;

DEPARTMENT_ID        CNT NAMES                                                                           
------------- ---------- --------------------------------------------------------------------------------
           10          1 ODCIVARCHAR2LIST('Jennifer')                                                    
           20          2 ODCIVARCHAR2LIST('Michael', 'Pat')                                              
           30          6 ODCIVARCHAR2LIST('Den', 'Karen', 'Guy', 'Sigal', 'Shelli', 'Alexander')         
           40          1 ODCIVARCHAR2LIST('Susan')                                                       
...

您还可以使用acursor() expression为每个结果行添加一个子查询,该查询更接近您的原始建议:

select d.department_id,
  count(e.employee_id) as cnt,
  cursor(
    select e2.first_name
    from employees e2
    where e2.department_id = d.department_id
  ) as names
from departments d
left join employees e on e.department_id = d.department_id
group by d.department_id;

SQL Developer将其结果(作为脚本运行时)显示为:

DEPARTMENT_ID        CNT NAMES                                                                           
------------- ---------- --------------------------------------------------------------------------------
           10          1 CURSOR STATEMENT : 3                                                            

CURSOR STATEMENT : 3

FIRST_NAME          
--------------------
Jennifer
           20          2 CURSOR STATEMENT : 3                                                            

CURSOR STATEMENT : 3

FIRST_NAME          
--------------------
Michael
Pat
           30          6 CURSOR STATEMENT : 3                                                            

CURSOR STATEMENT : 3

FIRST_NAME          
--------------------
Den
Alexander
Shelli
Sigal
Guy
Karen

6 rows selected. 

...

在这种情况下,由于您正在访问外部查询和子查询中的同一表,这些表可能效率较低;另一方面,您正在提取相同的数据块,因此可能无关紧要。但是,处理ref游标可能比集合更简单。在某种程度上取决于您现在如何处理结果。


您最初的建议基本上是没有cursor关键字的查询;但这会出错,因为a scalar subquery expression必须返回一个值:

select d.department_id,
  count(e.employee_id) as cnt,
  (
    select e2.first_name
    from employees e2
    where e2.department_id = d.department_id
  ) as names
from departments d
left join employees e on e.department_id = d.department_id
group by d.department_id;

ORA-01427: single-row subquery returns more than one row

......除非我(非常人为地)将其限制为我事先知道的部门,否则子查询中只会返回一行:

select d.department_id,
  count(e.employee_id) as cnt,
  (
    select e2.first_name
    from employees e2
    where e2.department_id = d.department_id
  ) as names
from departments d
left join employees e on e.department_id = d.department_id
where d.department_id in (10, 40)
group by d.department_id;

DEPARTMENT_ID        CNT NAMES                                                                           
------------- ---------- --------------------------------------------------------------------------------
           10          1 Jennifer                                                                        
           40          1 Susan                                                                           
相关问题