заменить каждый символ в строке на несколько символов - PullRequest
1 голос
/ 06 октября 2019

Мне нужно заменить значение "123" на JANFEBMAR. Примерно так: 1 заменен на JAN, 2 заменен на FEB ... и т. Д.

Я написал так select replace(replace(replace(replace('1234','1','JAN'),'2','FEB'),'3','MAR'),'4','APR') from dual; Это выглядит не очень чисто и может также повлиять на производительность. Как мне достичь через REGEXP_REPLACE?

Ответы [ 4 ]

0 голосов
/ 07 октября 2019

Установка Oracle :

CREATE TABLE test_data ( value ) AS
  SELECT '1234' FROM DUAL UNION ALL
  SELECT '1212' FROM DUAL UNION ALL
  SELECT '123456789101112' FROM DUAL UNION ALL
  SELECT '1 23456789101112' FROM DUAL UNION ALL
  SELECT '12AA34' FROM DUAL

Запрос 1 :

Наиболее эффективным решением будут вложенные REPLACE операторы и, если у вас нет большого количества заменяющих терминов, его будет проще всего просмотреть (если вы аккуратно отформатируете код):

SELECT value,
       REPLACE(
         REPLACE(
           REPLACE(
             REPLACE(
               REPLACE(
                 REPLACE(
                   REPLACE(
                     REPLACE(
                       REPLACE(
                         REPLACE(
                           REPLACE(
                             REPLACE(
                               value, '12', 'DEC'
                             ),
                             '11', 'NOV'
                           ),
                           '10', 'OCT'
                         ),
                         '9', 'SEP'
                       ),
                       '8', 'AUG'
                     ),
                     '7', 'JUL'
                   ),
                   '6', 'JUN'
                 ),
                 '5', 'MAY'
               ),
               '4', 'APR'
             ),
             '3', 'MAR'
           ),
           '2', 'FEB'
         ),
         '1', 'JAN'
       ) AS replaced_value
FROM   test_data;

Вывод :

VALUE            | REPLACED_VALUE                       
:--------------- | :------------------------------------
1234             | DECMARAPR                            
1212             | DECDEC                               
123456789101112  | DECMARAPRMAYJUNJULAUGSEPOCTNOVDEC    
1 23456789101112 | JAN FEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC
12AA34           | DECAAMARAPR                          

Запрос 2 :

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

WITH replacements ( item, replacement ) AS (
  SELECT  '1', 'JAN' FROM DUAL UNION ALL
  SELECT  '2', 'FEB' FROM DUAL UNION ALL
  SELECT  '3', 'MAR' FROM DUAL UNION ALL
  SELECT  '4', 'APR' FROM DUAL UNION ALL
  SELECT  '5', 'MAY' FROM DUAL UNION ALL
  SELECT  '6', 'JUN' FROM DUAL UNION ALL
  SELECT  '7', 'JUL' FROM DUAL UNION ALL
  SELECT  '8', 'AUG' FROM DUAL UNION ALL
  SELECT  '9', 'SEP' FROM DUAL UNION ALL
  SELECT '10', 'OCT' FROM DUAL UNION ALL
  SELECT '11', 'NOV' FROM DUAL UNION ALL
  SELECT '12', 'DEC' FROM DUAL UNION ALL
  SELECT 'AA', '???' FROM DUAL
),
list ( item, replacement, next_item, max_item ) AS (
  SELECT item,
         replacement,
         LAG( item ) OVER ( ORDER BY LENGTH( item ) ASC, item ASC ),
         MAX( item ) KEEP ( DENSE_RANK LAST ORDER BY LENGTH( item ), item ASC ) OVER ()
  FROM   replacements
),
replaced_values ( value, item ) AS (
  SELECT REPLACE( value, item, replacement ), next_item
  FROM   test_data
         CROSS JOIN
         list
  WHERE  item = max_item
UNION ALL
  SELECT REPLACE( value, r.item, replacement ), next_item
  FROM   replaced_values r
         INNER JOIN list l
         ON ( r.item = l.item )
  WHERE  r.item IS NOT NULL
)
SELECT value
FROM   replaced_values
WHERE  item IS NULL;

Выход :

| VALUE                                 |
| :------------------------------------ |
| DECMARAPR                             |
| DECDEC                                |
| DECMARAPRMAYJUNJULAUGSEPOCTNOVDEC     |
| JAN FEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC |
| DEC???MARAPR                          |

db <> скрипка здесь

0 голосов
/ 06 октября 2019

Вы все еще можете использовать regexp_replace с функцией to_date, содержащей MM и to_char функцию, содержащую MON в качестве второго аргумента:

with t(nr) as
(
 select '1234' from dual
), t2 as
(
select regexp_replace(
                      regexp_replace(nr,(substr(nr,level,1)),
                                     to_char(to_date(substr(nr,level,1),'MM'),'MON')), 
                                  '[^[:alpha:]]', '') str,
       level as lvl                          
  from t
 connect by level <= length(nr) 
)
select listagg(str) within group (order by lvl) as "String"
  from t2;

String
------------
JANFEBMARAPR

Демо

Кстати, вы можете преобразовать свой литерал «123» во что угодно, например «234», «342», «4587» и протестировать его в демоверсии. Метод работает до тех пор, пока вам нужны месяцы между января (1) и сентября (9) , как в текущем случае .

0 голосов
/ 07 октября 2019

Попытка разбить строку и затем преобразовать ее в название месяца проблематично по нескольким причинам:

  1. Скажем, у вас есть значение 1212. Нет никакого способа узнать, соответствует ли это DecDec, JanFebDec, DecJanFeb или JanFebJanFeb.
  2. У вас есть переменные длины ваших строк, так как некоторые месяцы состоят из 2 цифр, а некоторыеявляются 1-значными.
  3. Попытка разорвать и преобразовать столбец строк (или целые числа, представляющие строковые значения) по своей сути является плохим управлением данными. Он подвержен поломкам, ошибкам и дорогостоящим неэффективным запросам. Попробуй буквально любое другое решение, прежде чем прибегнуть к нему.

Первое, что спроси себя: Как эти данные загружаются в таблицу?

Каким-то образом вы находите целочисленное представление месяца, а затем соединяете его с другими месяцами. Почему бы не преобразовать его в название месяца вместо целого числа, прежде чем оно попадет в эту таблицу?

Второй вопрос, который нужно задать себе: Каков следующий шаг для этих данных?

Ряд строк, таких как "JanFebMar" и "MarFebSep""не особенно гибки, читабельны или полезны для многих. Скорее всего, вам придется преобразовать или разделить эти строки позже в вашем рабочем процессе, чтобы сделать их полезными.

Я бы настоятельно рекомендовал реструктурировать таблицу следующим образом:

Columns_with_your_other_data | MONTH1 | MONTH2 | MONTH3

Это позволяет вамчтобы сохранить месяцы организованными и различимыми, приведите их к целым числам или строкам, объедините их или разбейте на части, отсортируйте, отфильтруйте их, как вам угодно, избегая ошибок и дополнительной нагрузки на базу данных. Путь более гибкий! Намного менее подвержен ошибкам. Способ более читабельный. Лучший способ управления вашими данными.

Если вы абсолютно не можете ни при каких обстоятельствах реструктурировать таблицу (что кажется маловероятным), вам как минимум потребуется переформатировать значения целочисленного месяца, чтобы они состояли из двух цифр вместо одной цифры(т. е. янв = 01, а не 1), чтобы избежать анализа неправильного месяца.

0 голосов
/ 06 октября 2019

Вот один из вариантов:

SQL> with test (col) as
  2    (select '159' from dual),
  3  mon as
  4    (select to_char(to_date(lpad(regexp_substr(col, '\d', 1, level), 2, '0'), 'mm'), 'mon') val,
  5            level lvl
  6     from test
  7     connect by level <= length(col)
  8    )
  9  select listagg(val, '') within group (order by lvl) result
 10  from mon;

RESULT
--------------------------------------------------------------------------------
janmaysep

SQL>

Хотя, ваш вопрос несколько странный ;во что бы вы хотели преобразовать 1312? Это janmarjanfeb или janmardec?

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