Регулярные выражения в (Oracle) SQL для разделения даты / времени на отдельные столбцы даты и времени - PullRequest
2 голосов
/ 16 марта 2020

Я пытаюсь разделить дату и время из столбца, который содержит дату и время, используя регулярное выражение.

Ex-Current:

+------------------------------+
|           original           |
+------------------------------+
| 21-JAN-16 01.20.51.097679 PM |
| 02-FEB-16 01.32.53.469568 PM |
| 01-DEC-15 06.00.41.315710 AM |
| 14-JAN-16 04.11.53.107373 PM |
| 21-JAN-16 01.05.15.113135 PM |
| 03-FEB-16 12.06.10.128735 PM |
| 04-FEB-16 06.24.59.557983 PM |
+------------------------------+

Желаемый:

+-------------+-------------+
|    date     |    time     |
+-------------+-------------+
| 21-JAN-2016 | 01:20:51 PM |
| 02-FEB-2016 | 01:32:53 PM |
| 01-DEC-2015 | 06:00:41 AM |
| 14-JAN-2016 | 04:11:53 PM |
| 21-JAN-2016 | 01:05:15 PM |
| 03-FEB-2016 | 12:06:10 PM |
| 04-FEB-2016 | 06:24:59 PM |
+-------------+-------------+

Тип данных VARCHAR2.

Вот что я попробовал:

SELECT
REGEXP_REPLACE(original,
  '([[:digit:]]{2})\-([[:alpha:]]{3})\-([[:digit:]]{2})\ ([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{1,}\.([[:alpha:]]{2}))',
  '\1-\2-20\3') "date",

REGEXP_REPLACE(original,
  '([[:digit:]]{2})\-([[:alpha:]]{3})\-([[:digit:]]{2})\ ([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{1,}\.([[:alpha:]]{2}))',
  '\4:\5:\6 \7') "time"
FROM database.table

и вот что я получил, запустив выше:

+------------------------------+------------------------------+
|             date             |             time             |
+------------------------------+------------------------------+
| 21-JAN-16 01.20.51.097679 PM | 21-JAN-16 01.20.51.097679 PM |
| 02-FEB-16 01.32.53.469568 PM | 02-FEB-16 01.32.53.469568 PM |
| 01-DEC-15 06.00.41.315710 AM | 01-DEC-15 06.00.41.315710 AM |
| 14-JAN-16 04.11.53.107373 PM | 14-JAN-16 04.11.53.107373 PM |
| 21-JAN-16 01.05.15.113135 PM | 21-JAN-16 01.05.15.113135 PM |
| 03-FEB-16 12.06.10.128735 PM | 03-FEB-16 12.06.10.128735 PM |
| 04-FEB-16 06.24.59.557983 PM | 04-FEB-16 06.24.59.557983 PM |
+------------------------------+------------------------------+

Не слишком уверен, где я ошибся?

Ответы [ 5 ]

2 голосов
/ 16 марта 2020

Поскольку ваши данные очень регулярные, я бы вообще не использовал регулярное выражение. SUBSTR работает нормально.

-- sample data
with orig as (select '21-JAN-16 01.20.51.097679 PM' as dt from dual)
-- query
select substr(dt,1,9) as d, substr(dt,11) as t
from orig;

Но проблема в вашем регулярном выражении в том, что ваш последний \. должен быть \ - это пробел перед PM, а не точка. И вам не нужно выходить из пробелов, так что это может быть просто

'([[:digit:]]{2})\-([[:alpha:]]{3})\-([[:digit:]]{2}) ([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{1,} ([[:alpha:]]{2}))'
2 голосов
/ 16 марта 2020

Oracle имеет широкий спектр функций для работы с датой и временем. Как насчет этого, который уменьшает вашу проблему

SELECT TO_TIMESTAMP(original, 'DD-MON-RR HH:MI:SS.FF AM') as ORIGINAL_VALUE,
TO_TIMESTAMP(original, 'DD-MON-RR') AS DATE_ONLY,
TO_TIMESTAMP(original, 'HH:MI:SS.FF AM') AS TIME_ONLY
from database.table;
1 голос
/ 17 марта 2020

Не работайте с датами и временем как строками; Вы должны использовать соответствующие типы данных:

  • Использовать тип данных TIMESTAMP для даты и времени с долями секунды.
  • Использовать тип данных DATE для даты ( и, если необходимо, обрежьте компонент времени до полуночи.
  • Используйте тип данных INTERVAL, если вы хотите время без даты.

Итак, вы можете использовать:

SELECT TRUNC( datetime ) AS "date",
       datetime - TRUNC( datetime ) AS "time"
FROM   (
  SELECT TO_TIMESTAMP( original, 'DD-MON-RR HH12.MI.SS.FF6 AM' ) AS datetime
  FROM   table_name
)

Который выдаст (в зависимости от ваших настроек NLS):

date      | time                         
:-------- | :----------------------------
21-JAN-16 | +000000000 13:20:51.097679000
02-FEB-16 | +000000000 13:32:53.469568000
01-DEC-15 | +000000000 06:00:41.315710000
14-JAN-16 | +000000000 16:11:53.107373000
21-JAN-16 | +000000000 13:05:15.113135000
03-FEB-16 | +000000000 12:06:10.128735000
04-FEB-16 | +000000000 18:24:59.557983000

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

SELECT TO_CHAR( datetime, 'DD-MON-YYYY' ) AS "date",
       TO_CHAR( datetime, 'HH12.MI.SS AM' ) AS "time"
FROM   (
  SELECT TO_TIMESTAMP( original, 'DD-MON-RR HH12.MI.SS.FF6 AM' ) AS datetime
  FROM   table_name
)

, которая выводит:

date        | time       
:---------- | :----------
21-JAN-2016 | 01.20.51 PM
02-FEB-2016 | 01.32.53 PM
01-DEC-2015 | 06.00.41 AM
14-JAN-2016 | 04.11.53 PM
21-JAN-2016 | 01.05.15 PM
03-FEB-2016 | 12.06.10 PM
04-FEB-2016 | 06.24.59 PM

Чтобы ответить на вопрос, который вы задали:

Не знаете, где я ошибся?

У вас есть \. перед окончательным значением меридиана, когда вы должны иметь пробел и выходной шаблон для времени должен быть \4:\5:\6 \8:

SELECT REGEXP_REPLACE(
         original,
         '([[:digit:]]{2})\-([[:alpha:]]{3})\-([[:digit:]]{2})\ ([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{1,} ([[:alpha:]]{2}))',
         '\1-\2-20\3'
       ) "date",
       REGEXP_REPLACE(
         original,
         '([[:digit:]]{2})\-([[:alpha:]]{3})\-([[:digit:]]{2})\ ([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{1,} ([[:alpha:]]{2}))',
         '\4:\5:\6 \8'
       ) "time"
FROM   table_name

Выходы:

date        | time       
:---------- | :----------
21-JAN-2016 | 01:20:51 PM
02-FEB-2016 | 01:32:53 PM
01-DEC-2015 | 06:00:41 AM
14-JAN-2016 | 04:11:53 PM
21-JAN-2016 | 01:05:15 PM
03-FEB-2016 | 12:06:10 PM
04-FEB-2016 | 06:24:59 PM
* 10 47 * Вы можете упростить регулярное выражение, избавившись от ненужных escape-символов и используя группы символов perl в стиле, а не POSIX:
^(\d{2})-([A-Z]{3})-(\d{2}) (\d{2})\.(\d{2})\.(\d{2})\.(\d{1,}) ([AP]M)$

db <> fiddle здесь

0 голосов
/ 16 марта 2020

Если ваш столбец original равен varchar2, вам необходимо преобразовать его в timestamp и затем отдельно по дате и времени следующим образом:

select 
to_char(to_timestamp(original,'dd-mon-rr hh.mi.ss.ff PM'), 'dd-mon-rr') as "DATE",
to_char(to_timestamp(original,'dd-mon-rr hh.mi.ss.ff PM'), 'hh:mi:ss PM') as "TIME",
from your_table

Cheers !!

0 голосов
/ 16 марта 2020
select 
to_char(to_timestamp(s, 'dd-MON-RR hh12.mi.ss.ff6 PM'), 'dd-MON-yyyy') s1, 
to_char(to_timestamp(s, 'dd-MON-RR hh12.mi.ss.ff6 PM'), 'hh12.mi.ss PM') s2
from t
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...