Есть ли способ сделать это простым запросом? - PullRequest
0 голосов
/ 16 февраля 2020

У меня есть БОЛЬШАЯ (23 миллиона строк) таблица CONTRATOS, в которой есть следующие столбцы:

SELECT
    CONTRATO,
    CODIGO_ORIGEN,
    ORIGEN
FROM CONTRATOS;

Есть несколько строк с одинаковым значением для столбца CODIGO_ORIGEN:

CODIGO_ORIGEN     CONTRATO       ORIGEN 
-------------    ----------     --------
     1               345          CONT
     1               363          BKP
     1               645          BKP
     1               365          CONT 

Мне нужно получить только один CONTRATO для каждого CODIGO_ORIGEN, но всегда отдавать приоритет значению 'CONT' . Таким образом, в этом примере это будет CONTRATO со значением 365

Я пытался что-то вроде этого:

SELECT  
   CODIGO_ORIGEN,
   CASE
    WHEN ORIGEN = 'CONT' THEN
        FIRST_VALUE (CONTRATO) OVER (PARTITION BY CODIGO_ORIGEN ORDER BY CONTRATO DESC) 
    WHEN ORIGEN = 'BKP' THEN
        FIRST_VALUE (CONTRATO) OVER (PARTITION BY CODIGO_ORIGEN ORDER BY CAMPO1 DESC, CAMPO2 DESC, CONTRATO DESC) 
   END AS CONTRATO
FROM CONTRATOS;

В случае, если ORIGEN это 'CONTR', я должен отсортировать по наибольшему CONTRATO и получить столбец CONTRATO. В случае, если ORIGEN 'BKP', я должен отсортировать по разным столбцам и получить столбец CONTRATO.

Наконец, я должен иметь одну уникальную строку с CODIGO_ORIGEN -> CONTRATO (1: 1).

Есть ли простой способ сделать это (1 запрос)?

Заранее спасибо!

Ответы [ 3 ]

0 голосов
/ 17 февраля 2020

Я думаю, что получил:

      SELECT 
        C.contrato, C.codigo_origen,  NVL2(MX.CONTRATO, NULL, 'F') AS ESTADO, NVL2(MX.CONTRATO, NULL, '4008') AS ERROR
      FROM MGR_CUENTA_CTL C
      LEFT JOIN
      (
          select /*+ PARALLEL */
            DISTINCT CODIGO_ORIGEN,
            CASE
              WHEN ORIGEN_TABLA = 'CONT' THEN FIRST_VALUE(CONTRATO) OVER (PARTITION BY CODIGO_ORIGEN ORDER BY CONTRATO DESC) 
              WHEN ORIGEN_TABLA = 'BKP' THEN FIRST_VALUE(CONTRATO) OVER (PARTITION BY CODIGO_ORIGEN ORDER BY CAMPO1 DESC, CAMPO2 DESC) 
            END AS CONTRATO
          FROM
          (  
             SELECT /*+ PARALLEL */ ctl.*
             FROM mgr_cuenta_ctl ctl
             LEFT JOIN 
             (
                   select /*+ PARALLEL */ CODIGO_ORIGEN
                   from mgr_cuenta_ctl
                   group by codigo_origen having count(distinct origen) > 1
             ) CTL2 ON ctl.CODIGO_ORIGEN = ctl2.CODIGO_ORIGEN
             WHERE (CTL2.CODIGO_ORIGEN IS NOT NULL AND CTL.ORIGEN <> 'BKP') OR (CTL2.CODIGO_ORIGEN IS NULL)
          )
      ) MX ON MX.CONTRATO = C.CONTRATO;

Я думаю, что он делает то, что пытался объяснить.

Имеет ли это смысл для вас?

Спасибо!

0 голосов
/ 17 февраля 2020

Проверьте, есть ли origen = 'CONT', используйте analyti c count(). Если да - используйте функцию analyti c с первым методом упорядочения, если нет - вторым упорядочением:

select mgr_cuenta_ctl.*,
       case 
       when count(case origen when 'CONT' then 1 end) over (partition by codigo_origen) > 0
       then first_value(contrato) over (
            partition by codigo_origen
            order by case origen when 'CONT' then 1 end, contrato desc)
       else first_value(contrato) over (
            partition by codigo_origen
            order by case origen when 'BKP' then 1 end, campo1 desc, campo2 desc, contrato desc)
       end as best
  from mgr_cuenta_ctl

Если вам нужны только сгруппированные значения, без подробностей, удалите пункты partition by:

select codigo_origen,
       case when count(case origen when 'CONT' then 1 end) > 0
       then max(contrato) keep (dense_rank first 
                order by case origen when 'CONT' then 1 end, contrato desc) 
       else max(contrato) keep (dense_rank first 
                order by case origen when 'BKP' then 1 end, campo1 desc, campo2 desc) 
       end as best
  from mgr_cuenta_ctl
  group by codigo_origen

Демоверсия dbfiddle

Кстати. Я попытался проанализировать ваш запрос, но он выдал несколько странных ошибок, и я сдался. Здесь вы дотрагиваетесь до таблицы только один раз, без самообъединений, поэтому она должна быть быстрее.

0 голосов
/ 16 февраля 2020

Вы можете использовать ROW_NUMBER для ранжирования ваших строк по codigo_origen. Вот один из способов написать предложение ORDER BY. Есть и другие.

select *
from
(
  select  
     c.*,
     row_number() over (
       partition by codigo_origen 
       order by 
         case when origen 'BKP' then campo1 end desc,
         case when origen 'BKP' then campo2 end desc,
         origen desc
     ) as rn
  from contratos c
) ranked
where rn = 1
order by codigo_origen;
...