Подзапрос для улучшения производительности и удобства обслуживания - PullRequest
0 голосов
/ 03 марта 2020

У меня есть запрос, подобный следующему:

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 для каждого результата запроса, приведенного выше, я хочу получить некоторую информацию, которая доступна в Таблице3, в основном, мне нужны все значения в Table3.column1 для каждого КОДА (Table1.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)

И вот так, я могу получить все значения Table3.column1 для каждого Table1.column1 (для каждый код) первого запроса.

Сомнение

Оба запроса практически одинаковы. Единственное различие между запросом перед foreach и запросом внутри foreach заключается в части SELECT и в WHERE, в основном там, где просто есть дополнительное условие. Таким образом, это не очень хорошо с точки зрения производительности, а обслуживаемость имеет 2 запроса, потому что оба запроса практически одинаковы. Таким образом, должна быть возможность получить всю необходимую информацию в первом запросе вместо того, чтобы иметь второй запрос внутри foreach.

Знаете ли вы, что необходимо изменить в первом запросе, кроме возврата CODE и COUNT, также возвращают все значения в Table3.column1 для каждого CODE? Так что же можно удалить запрос внутри foreach и получить все, что нужно, только одним запросом (1-й запрос)? Необходимый вывод для первого запроса должен выглядеть примерно так:

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 Ответ

1 голос
/ 03 марта 2020

Возможно, вы ищете функцию collect() , которая дает вам вложенную таблицу как часть вашего основного набора результатов.

Вот пример использования данных схемы 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')                                                       
...

Вы также можете использовать a cursor() выражение , чтобы включить подзапрос для каждой строки результатов, который ближе к вашему первоначальному предложению:

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 Разработчик представляет результаты этого (при запуске в виде скрипта) как:

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; но это приведет к ошибке, потому что скалярное выражение подзапроса должно возвращать одно значение:

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                                                                           
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...