Объяснение Oracle PARTITION BY против GROUP BY для похожих результатов - PullRequest
4 голосов
/ 26 марта 2019

У меня есть следующая таблица (Знаки):

firstname    lastname    Mark    
------------------------------
arun         prasanth    40      
ann          antony      45      
sruthy       abc         41      
new          abc         47      
arun         prasanth    45      
arun         prasanth    49      
ann          antony      49      

И хотел бы добавить столбец с тегами, если запись с конкретными столбцами встречается более одного раза. Это результат:

firstname    lastname    Mark    MULTI_FLAG
----------------------------------------------
arun         prasanth    40      1
ann          antony      45      1
sruthy       abc         41      0
new          abc         47      0
arun         prasanth    45      1
arun         prasanth    49      1
ann          antony      49      1

Я могу получить результат с помощью следующего GROUP BY запроса:

SELECT M1.firstname
      ,M1.lastname
      ,M1.Mark
      ,M2.MULTI_COUNT
FROM Marks  M1
JOIN (SELECT firstname, lastname, CASE WHEN COUNT (*) > 1 THEN 1 ELSE 0 END AS MULTI_COUNT
    FROM Marks
    GROUP BY firstname, lastname) M2
   ON M2.firstname = M1.firstname AND M2.lastname = M1.lastname;

Или намного красивее PARTITION BY запрос:

SELECT
  firstname,
  lastname,
  CASE WHEN COUNT(*) OVER (PARTITION BY
     firstname,
     lastname) > 1 THEN 1 ELSE 0 END AS MULTI_FLAG
FROM
  Marks

Выполнение запроса GROUP BY для аналогичной большой таблицы, возвращенной в: 34 м 56 с 595 мс

Выполнение запроса PARTITION BY для аналогичной большой таблицы, возвращенной в:

  1. Первый запуск: 55 м 47 с 851 мс
  2. Второй запуск: 36 м 46 с 95 мс

Мне было бы интересно узнать:

  1. Лучший способ добиться моих результатов
  2. Что объясняет разницу в производительности.
  3. РЕДАКТИРОВАТЬ: Как читать план запроса.

EDIT: Версия Oracle Oracle Database 11g Enterprise Edition, выпуск 11.2.0.3.0 - 64-разрядная версия PL / SQL Release 11.2.0.3.0 - Производство "CORE 11.2.0.3.0 Production" TNS для Linux: версия 11.2.0.3.0 - производство NLSRTL Версия 11.2.0.3.0 - Производство

РАЗДЕЛЕНИЕ ПО ПЛАНУ

PLAN_TABLE_OUTPUT
Plan hash value: 3822227444

---------------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                       | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                            |   668K|    90M|       | 90429   (1)| 00:18:06 |
|   1 |  WINDOW SORT                   |                            |   668K|    90M|    98M| 90429   (1)| 00:18:06 |
|*  2 |   HASH JOIN RIGHT OUTER        |                            |   668K|    90M|       | 69340   (1)| 00:13:53 |
|   3 |    TABLE ACCESS FULL           | COUNTRY_REGION_MAPPINGS    |   177 |  4779 |       |     3   (0)| 00:00:01 |
|   4 |    NESTED LOOPS                |                            |       |       |       |            |          |
|   5 |     NESTED LOOPS               |                            |   377K|    41M|       | 69335   (1)| 00:13:53 |
|   6 |      MAT_VIEW ACCESS FULL      | PROJINFO_MAX_ITER_MVW      | 17713 |   328K|       |   782   (1)| 00:00:10 |
|*  7 |      INDEX RANGE SCAN          | Q_CLIN_ASSUM_BYCOUN_PK     |     1 |       |       |     3   (0)| 00:00:01 |
|   8 |     TABLE ACCESS BY INDEX ROWID| Q_CLINICAL_ASSUM_BYCOUNTRY |    21 |  2016 |       |     4   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(UPPER("CRM"."COUNTRY"(+))=UPPER("QCAB"."TRIAL_COUNTRY"))
   7 - access("PMIM"."OPPORTUNITYNUM"="QCAB"."OPPORTUNITYNUM" AND "PMIM"."CONTRACTNUM"="QCAB"."CONTRACTNUM" 
              AND "PMIM"."ITERATION"="QCAB"."ITERATION")
       filter(UPPER("QCAB"."SHEET_LOC") LIKE '%COUNTRY ASSUMPTIONS%' OR UPPER("QCAB"."SHEET_LOC") LIKE 
              'INPUT%')

GROUP BY Plan

PLAN_TABLE_OUTPUT
Plan hash value: 648231064

------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name                       | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |                            |   912 |  2052K|       |   226K  (1)| 00:45:22 |
|*  1 |  HASH JOIN                        |                            |   912 |  2052K|       |   226K  (1)| 00:45:22 |
|   2 |   TABLE ACCESS FULL               | COUNTRY_REGION_MAPPINGS    |   177 |  4779 |       |     3   (0)| 00:00:01 |
|*  3 |   HASH JOIN                       |                            | 89667 |   194M|    45M|   226K  (1)| 00:45:22 |
|   4 |    NESTED LOOPS                   |                            |       |       |       |            |          |
|   5 |     NESTED LOOPS                  |                            |   377K|    41M|       | 69335   (1)| 00:13:53 |
|   6 |      MAT_VIEW ACCESS FULL         | PROJINFO_MAX_ITER_MVW      | 17713 |   328K|       |   782   (1)| 00:00:10 |
|*  7 |      INDEX RANGE SCAN             | Q_CLIN_ASSUM_BYCOUN_PK     |     1 |       |       |     3   (0)| 00:00:01 |
|   8 |     TABLE ACCESS BY INDEX ROWID   | Q_CLINICAL_ASSUM_BYCOUNTRY |    21 |  2016 |       |     4   (0)| 00:00:01 |
|   9 |    VIEW                           |                            |   668K|  1377M|       | 86518   (1)| 00:17:19 |
|  10 |     HASH GROUP BY                 |                            |   668K|    72M|    80M| 86518   (1)| 00:17:19 |
|* 11 |      HASH JOIN RIGHT OUTER        |                            |   668K|    72M|       | 69340   (1)| 00:13:53 |
|  12 |       TABLE ACCESS FULL           | COUNTRY_REGION_MAPPINGS    |   177 |  2478 |       |     3   (0)| 00:00:01 |
|  13 |       NESTED LOOPS                |                            |       |       |       |            |          |
|  14 |        NESTED LOOPS               |                            |   377K|    35M|       | 69335   (1)| 00:13:53 |
|  15 |         MAT_VIEW ACCESS FULL      | PROJINFO_MAX_ITER_MVW      | 17713 |   328K|       |   782   (1)| 00:00:10 |
|* 16 |         INDEX RANGE SCAN          | Q_CLIN_ASSUM_BYCOUN_PK     |     1 |       |       |     3   (0)| 00:00:01 |
|  17 |        TABLE ACCESS BY INDEX ROWID| Q_CLINICAL_ASSUM_BYCOUNTRY |    21 |  1701 |       |     4   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("R2"."TRIAL_COUNTRY_CD"="CRM"."COUNTRY_CD" AND 
              UPPER("CRM"."COUNTRY")=UPPER("QCAB"."TRIAL_COUNTRY"))
   3 - access("R2"."OPPORTUNITYNUM"="QCAB"."OPPORTUNITYNUM" AND "R2"."ITERATION"="QCAB"."ITERATION" AND 
              "R2"."CONTRACTNUM"="QCAB"."CONTRACTNUM" AND "R2"."ASSUMPTION"="QCAB"."ASSUMPTION")
   7 - access("PMIM"."OPPORTUNITYNUM"="QCAB"."OPPORTUNITYNUM" AND "PMIM"."CONTRACTNUM"="QCAB"."CONTRACTNUM" AND 
              "PMIM"."ITERATION"="QCAB"."ITERATION")
       filter(UPPER("QCAB"."SHEET_LOC") LIKE '%COUNTRY ASSUMPTIONS%' OR UPPER("QCAB"."SHEET_LOC") LIKE 'INPUT%')
  11 - access(UPPER("CRM"."COUNTRY"(+))=UPPER("QCAB"."TRIAL_COUNTRY"))
  16 - access("PMIM"."OPPORTUNITYNUM"="QCAB"."OPPORTUNITYNUM" AND "PMIM"."CONTRACTNUM"="QCAB"."CONTRACTNUM" AND 
              "PMIM"."ITERATION"="QCAB"."ITERATION")
       filter(UPPER("QCAB"."SHEET_LOC") LIKE '%COUNTRY ASSUMPTIONS%' OR UPPER("QCAB"."SHEET_LOC") LIKE 'INPUT%')

1 Ответ

2 голосов
/ 26 марта 2019

Обычно вы начинаете с аналитической функции count(*), которая приводит к компактному SQL.

Недостатком этого подхода является то, что данные должны быть отсортированы (см. WINDOW SORT операция).Подход GROUP BY позволяет избежать сортировки, поскольку может использоваться HASH GROUP BY, что может привести к повышению производительности.

Ваш пример немного сложнее, поскольку вы используете не таблицу, а представление, объединяющее тритаблицы - это соединение выполняется дважды, для GROUP BY и для подробных данных;что, конечно, не оптимально.

Поэтому я начну с версии запроса для аналитической функции (возможно с опцией PARALLEL).

Если вы хотите попробовать GROUP BY возможна легкая версия:

1) сгруппировать только дублированные ключи

2) сделать OUTER JOIN, чтобы присвоить MULTI_FLAG

пример с планом выполнения ниже -простой тест с вашими данными

with dups as (
select firstname,lastname  from tmp
group by firstname,lastname
having count(*) > 1)
select tmp.FIRSTNAME, tmp.LASTNAME, tmp.MARK,
case when dups.firstname is not NULL then 1 else 0 end as MULTI_FLAG
from tmp
left outer join dups on tmp.firstname = dups.firstname and tmp.lastname = dups.lastname;

Вам все равно нужно будет дважды получить доступ к своему представлению, но окончательное соединение будет быстрее (особенно если у вас есть только небольшое количество дублированных ключей).

--------------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |   105K|    26M|       |  1673   (1)| 00:00:21 |
|*  1 |  HASH JOIN RIGHT OUTER|      |   105K|    26M|    11M|  1673   (1)| 00:00:21 |
|   2 |   VIEW                |      |   105K|    10M|       |   128   (4)| 00:00:02 |
|*  3 |    FILTER             |      |       |       |       |            |          |
|   4 |     HASH GROUP BY     |      |   105K|    10M|       |   128   (4)| 00:00:02 |
|   5 |      TABLE ACCESS FULL| TMP  |   105K|    10M|       |   125   (1)| 00:00:02 |
|   6 |   TABLE ACCESS FULL   | TMP  |   105K|    15M|       |   125   (1)| 00:00:02 |
--------------------------------------------------------------------------------------
...