Как оптимизировать отображение по нескольким таблицам - PullRequest
0 голосов
/ 11 июня 2018

Я пытаюсь оптимизировать мой код.Решение, описанное ниже, работает нормально, но я уверен, что есть лучшие способы сделать это.Есть ли у вас какие-либо рекомендации?

У меня есть одна таблица с бизнес-контрактами и некоторыми характерными атрибутами:

table_contracts
contract_number       attribute_1        attribute_2        attribute_3
123                         a                  e                   t
456                         a                  f                   s
789                         b                  g                   s

И вторая таблица, которая отображает каждый контракт в определенную группу.Эти группы имеют разные приоритеты (большее число => более высокий приоритет).Если столбец атрибута является пустым, это означает, что он не требуется (=> m3 является отображением перехвата всех)

table_mappings
map_number    priority    attribute_1        attribute_2        attribute_3
m1                5           a                  e                   t
m2                4           a
m3                3    

В результате мне нужен номер контракта и соответствующий номер карты с наивысшим приоритетом.

Вот как я это сделал, это работает, но кто-нибудь знает, как это оптимизировать?

with 
first_selection as 
  (
    select
    table_contracts.contract_number
    ,table_mappings.priority
    ,row_number() over(partition by table_contracts.contract_number order by table_mappings.priority desc)
    from table_contracts
    left join table_mappings
        on (table_contracts.attribute_1 = table_mappings.attribute_1 or table_mappings.attribute_1 is null)
        and (table_contracts.attribute_2 = table_mappings.attribute_2 or table_mappings.attribute_2 is null)
        and (table_contracts.attribute_3 = table_mappings.attribute_3 or table_mappings.attribute_3 is null)
   ),
second_selection as
   (
    select
    table_contracts.contract_number
    ,table_mappings.priority
    ,table_mappings.map_number
    from table_contracts
    left join table_mappings
        on (table_contracts.attribute_1 = table_mappings.attribute_1 or table_mappings.attribute_1 is null)
        and (table_contracts.attribute_2 = table_mappings.attribute_2 or table_mappings.attribute_2 is null)
        and (table_contracts.attribute_3 = table_mappings.attribute_3 or table_mappings.attribute_3 is null)
   )
select
first_selection.contract_number 
,second_selection.map_number
from first_selection
join second_selection 
    on first_selection.contract_number = second_selection.contract_number and first_selection.priority = second_selection.priority 
where first_selection.rn = 1

Вывод этого кода будет:

Results
contract_number       map_number
123                       m1
456                       m2
789                       m3

Ответы [ 3 ]

0 голосов
/ 11 июня 2018

Попробуйте приведенную ниже логику, используя версию CTE, аналогичную вашей.Надеюсь, это поможет!

Демо

 WITH contracts AS
        (SELECT 123 AS contract_number, 'a' AS attribute_1, 'e' AS attribute_2, 't' AS attribute_3 FROM   dual 
         UNION 
         SELECT 456, 'a', 'f', 's' FROM   dual 
         UNION SELECT 789, 'b', 'g', 's' FROM   dual
        ), 
 mappings AS
        (SELECT 'm1' AS map_number, 5  AS priority, 'a'  AS attribute_1, 'e'  AS attribute_2, 't'  AS attribute_3 FROM   dual 
         UNION 
         SELECT 'm2', 4, 'a', NULL, NULL FROM   dual 
         UNION 
         SELECT 'm3', 3, NULL, NULL, NULL FROM   dual
        ), 
 prioritymap AS
        (SELECT contract_number, 
                map_number, 
                Rank() over(PARTITION BY contracts.contract_number ORDER BY mappings.priority DESC) AS rank 
          FROM contracts 
               JOIN mappings 
                  ON ( contracts.attribute_1 = mappings.attribute_1 OR mappings.attribute_1 IS NULL ) 
                     AND ( contracts.attribute_2 = mappings.attribute_2 OR mappings.attribute_2 IS NULL ) 
                     AND ( contracts.attribute_3 = mappings.attribute_3 OR mappings.attribute_3 IS NULL )
        )
SELECT contract_number, map_number 
FROM   prioritymap 
WHERE  prioritymap.rank = 1
0 голосов
/ 11 июня 2018

Вы можете просто объединить таблицы при заданном условии (атрибут равен нулю в таблице сопоставления или должен соответствовать атрибуту в таблице контрактов).Затем агрегируйте по номеру контракта, чтобы получить лучший номер_карты.

select
  c.contract_number,
  max(m.map_number) keep (dense_rank last order by m.priority) as map_number
from table_contracts c
join table_mappings m
  on  (m.attribute_1 is null or m.attribute_1 = c.attribute_1)
  and (m.attribute_2 is null or m.attribute_2 = c.attribute_2)
  and (m.attribute_3 is null or m.attribute_3 = c.attribute_3)
group by c.contract_number
order by c.contract_number;

В любом случае, вы делаете это для всех контрактов, и сопоставление может совпадать для любой комбинации атрибутов, так что это приведет к полному сканированию таблицы.Единственный способ увидеть это быстрее - параллельное исключение.Возможно, СУБД настроена на это автоматически, в противном случае вы можете использовать подсказку:

select /*+parallel(4)*/
...
0 голосов
/ 11 июня 2018

Я думаю, вам нужен только один из вариантов:

with prioritized as(
   select c.contract_number, c.attribute_1, c.attribute_2, c.attribute_3, m.map_number
         ,row_number() over(
            partition by c.contract_number
                order by m.priority desc
         ) as rn
     from table_contracts     c 
     left join table_mappings m on(
          (c.attribute_1 = m.attribute_1 or m.attribute_1 is null)
      and (c.attribute_2 = m.attribute_2 or m.attribute_2 is null)
      and (c.attribute_3 = m.attribute_3 or m.attribute_3 is null)       
     )
)
select * 
  from prioritized
 where rn = 1 
...