Переключить значения столбца влево - PullRequest
0 голосов
/ 10 июля 2020

У меня есть таблица, как показано ниже:

      Column A           Column B            Column C
     ----------          ---------         -----------
         1                   1                 2
         4                   3                 4
         3                  null               2
         12                  12                12
         15                  7                 7 
         8                   9                 6
         null                2                 2
         null                null              3

Мне нужно переместить каждое значение столбца справа налево, пока каждый столбец не будет иметь разные значения, кроме нулевых значений. Вывод должен быть таким:

      Column A           Column B            Column C
     ----------          ---------         -----------
         1                   2                null
         4                   3                null
         3                   2                null
         12                  null             null
         15                  7                null 
         8                   9                6
         2                   null             null
         3                   null             null

Как я могу сделать это самым простым способом?

Спасибо,

Ответы [ 4 ]

1 голос
/ 10 июля 2020

Вы можете использовать regexp_substr следующим образом:

Табличные данные:

SQL> select a,b,c from t;

   A    B    C
---- ---- ----
   1    1    2
   4    3    4
   3         2
  12   12   12
  15    7    7
   8    9    6
        2    2
             3

8 rows selected.

Ваш запрос:

SQL> select regexp_substr(vals,'[^,]+',1,1) as a,
  2         regexp_substr(vals,'[^,]+',1,2) as b,
  3         regexp_substr(vals,'[^,]+',1,3) as c
  4    from (select rtrim(case when a is not null then a || ',' end ||
  5     case when b = a or b is null then null else b || ',' end ||
  6     case when b = c or a = c or c is null then null else c end, ',') as vals
  7    from t
  8  );

A   B   C
--- --- ---
1   2
4   3
3   2
12
15  7
8   9   6
2
3

8 rows selected.

SQL>
1 голос
/ 10 июля 2020

Эта комбинация unpivot, pivot и условного порядка возвращает желаемый результат:

with 
  nn as (
   select id, col, case rn when 1 then val end val
   from (
     select id, col, val, row_number() over (partition by id, val order by col) rn
     from (select rownum id, a, b, c from t)
     unpivot (val for col in (a, b, c) ) ) ),
   mov as (
     select id, val, 
            row_number() over (partition by id 
                               order by case when val is not null then col end ) rn 
     from nn )
select * from mov pivot (max(val) for rn in (1 a, 2 b, 3 c))

dbfiddle

Подзапрос nn удаляет повторяющиеся значения, подзапрос mov на основе условного упорядочивания перемещает их вверх. Затем pivot перемещает строки в столбцы, потому что они не были развернуты на первом шаге.

1 голос
/ 10 июля 2020

Если вход (и выход) - три столбца, как в вашем примере, то небольшой перебор может дать вам эффективный запрос:

select coalesce(a, b, c)                                 as a
     , case when b != a                       then b
            when c != coalesce(a, b)          then c end as b
     , case when a != b and b != c and a != c then c end as c
from   t
;

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

Это нелегко обобщить; если бы у вас было, скажем, восемь столбцов на входе (и на выходе), вам было бы лучше с решением, подобным предложенному Ponder Stibbons. Однако обратите внимание, что количество столбцов (три, восемь или 250) должно быть известно заранее для стандартного запроса SQL; в противном случае вам нужно будет написать динамический c запрос, что обычно не считается хорошей практикой.

EDIT :

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

Это решение аналогично тому, что опубликовал Ponder Stibbons. Есть два основных отличия. Во-первых, я использую предложение lateral, которое доступно в Oracle 12.1 и выше; это позволяет выполнять вычисления отдельно в каждой строке (вместо того, чтобы смешивать все значения из всех строк вместе после unpivot, только для того, чтобы сгруппировать их обратно в исходные строки через pivot). Во-вторых, код немного сложнее, чтобы обрабатывать случай, когда все значения в строке null.

select l.a, l.b, l.c
from   t,
  lateral (
    select a, b, c
    from   ( select  1, row_number() over (order by nvl2(v, min(o), null)) o, v
             from    ( select t.a, t.b, t.c from dual )
             unpivot include nulls (v for o in (a as 1, b as 2, c as 3))
             group   by v 
          ) 
    pivot (max(v) for o in (1 as a, 2 as b, 3 as c))
  ) l
;

EDIT 2

В комментариях после этого ответа ОП заявляет, что его реальные данные имеют пять столбцов, а не три. Первое решение сложно расширить до пяти столбцов, но второе решение легко расширить. Показываю как на dbfiddle .

0 голосов
/ 10 июля 2020

Хммм. . . один из методов - построить строку, а затем разбить ее на части. Например:

select regexp_substr(str, '[^|]+', 1, 1) as a,
       regexp_substr(str, '[^|]+', 1, 2) as b,
       regexp_substr(str, '[^|]+', 1, 3) as c
from (select t.*,
             trim(leading '|' from
                  (case when a is not null then '|' || a end) ||
                  (case when b is not null then '|' || b end) ||
                  (case when c is not null then '|' || c end)
                 ) str
      from t
     ) t

Здесь - скрипт db <>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...