2015-09-07 12 views
6

Ho un dato come di seguito con scheda limitata tra di loro. Li ho rappresentato in vista quiconversione dei dati con regexp in oracle sql

with t_view as (select '6-21 6-21 6-21 6-21 6-21 6-21 6-21 ' as col from dual 
union 
select '6-20 6-20 6-20 6-20 6-20 ' from dual 
union 
select '6-9 6-9 6-9 6-9 6-9 6-9 6-9 ' from dual) 

mio risultato atteso è

Mon: 6-21, Tue: 6-21, Wed: 6-21, Thu: 6-21, Fri: 6-21, Sat: 6-21, Sun: 6-21 
Mon: 6-20, Tue: 6-20, Wed: 6-20, Thu: 6-20, Fri: 6-20 
Mon: 6-9, Tue: 6-9, Wed: 6-9, Thu: 6-9, Fri: 6-9, Sat: 6-9, Sun: 6-9 

ho pensato di sostituire tutti coloro tavola orizzontale con alcuni modelli unici come questo e quindi sostituire quel modello con Mon, basata Tue sull'indicizzazione

$ 1 (6-20) $ 2 (6-20) $ 3 (6-20) $ 4 (6-20) $ 5 (6-20)

Ho provato la domanda sotto ma non poteva completarlo

select regexp_replace(col, '([[:digit:]]-[[:digit:]]{2}[[:space:]]+)','$(\1)') from t_view; 
+0

Non è possibile utilizzare una sostituzione regex qui dal momento che richiede 2 diverse stringhe di sostituzione (come il numero di giorni non è costante). –

risposta

1

Perché non possiamo usare questo modo semplice? Sembra buono come per me

SELECT 'Mon: '||regexp_substr(col,'\d+\-\d+',1,1) || 
     ', Tue: '||regexp_substr(col,'\d+\-\d+',1,2) || 
     ', Wed: '||regexp_substr(col,'\d+\-\d+',1,3) || 
     ', Thu: '||regexp_substr(col,'\d+\-\d+',1,4) || 
     ', Fri: '||regexp_substr(col,'\d+\-\d+',1,5) || 
     ', Sat: '||regexp_substr(col,'\d+\-\d+',1,6) || 
     ', Sun: '||regexp_substr(col,'\d+\-\d+',1,7) 
FROM t_view 

Ovviamente è facile eliminare vuota sab dom, ad esempio con NVL2:

SELECT 'Mon: '||regexp_substr(col,'\d+\-\d+',1,1) || 
     ', Tue: '||regexp_substr(col,'\d+\-\d+',1,2) || 
     ', Wed: '||regexp_substr(col,'\d+\-\d+',1,3) || 
     ', Thu: '||regexp_substr(col,'\d+\-\d+',1,4) || 
     ', Fri: '||regexp_substr(col,'\d+\-\d+',1,5) || 
     nvl2(regexp_substr(col,'\d+\-\d+',1,6), 
     ', Sat: '||regexp_substr(col,'\d+\-\d+',1,6) || 
     ', Sun: '||regexp_substr(col,'\d+\-\d+',1,7),null) 
FROM t_view 

Si dovrebbe tenere a mente, che questo è solo esempio, e se è possibile ottenere dati con un numero qualsiasi di giorno ha mancato si dovrebbe usare NVL2 in più luoghi

+0

Ciò lascerà i valori concatenati supplementari in cui il numero di valori è inferiore a 7. Potrebbe essere gestito utilizzando l'espressione caso. –

+0

Questo includerà 'Sat' e' Sun' anche se non ci sono abbastanza valori nell'input per includerli. – MT0

+0

Penso che possiamo evitare questo aggiungendo la funzione di sostituzione sopra il risultato ottenuto e sostituendo le stringhe che non hanno cifre associate con esso, nel nostro caso sarà Sat and Sun – arunb2w

3

avete bisogno di una combinazione di CASE espressione, REGEXP_COUNT e REGEXP_REPLACE poiché non hai la stessa espressione per tutte le righe. A seconda dei dati, è possibile avere il numero di condizioni nell'espressione del caso.

Il modello di espressione regolare è (\d-\d+ ).

Per esempio,

SQL> WITH t_view(col) AS 
    2 (SELECT '6-21 6-21 6-21 6-21 6-21 6-21 6-21 ' FROM dual 
    3 UNION 
    4 SELECT '6-20 6-20 6-20 6-20 6-20 ' FROM dual 
    5 UNION 
    6 SELECT '6-9 6-9 6-9 6-9 6-9 6-9 6-9 ' FROM dual 
    7 ) 
    8 SELECT REPLACE(new_col, ' ','') new_col 
    9 FROM (
10 SELECT 
11  CASE 
12  WHEN regexp_count(col, '\d+\-\d+') = 5 
13  THEN regexp_replace(col, 
14       '(\d-\d+ )(\d-\d+ )(\d-\d+ )(\d-\d+ )(\d-\d+ )', 
15       'Mon: \1, Tue: \2,Wed: \3,Thu: \4,Fri: \5') 
16  WHEN regexp_count(col, '\d+\-\d+') = 7 
17  THEN regexp_replace(col, 
18       '(\d-\d+ )(\d-\d+ )(\d-\d+ )(\d-\d+ )(\d-\d+ )(\d-\d+ )(\d-\d+ )', 
19       'Mon: \1, Tue: \2,Wed: \3,Thu: \4,Fri: \5,Sat: \6,Sun: \7') 
20  END new_col 
21 FROM t_view 
22 ); 

NEW_COL 
---------------------------------------------------------------------------------------------------- 
Mon: 6-20, Tue: 6-20,Wed: 6-20,Thu: 6-20,Fri: 6-20 
Mon: 6-21, Tue: 6-21,Wed: 6-21,Thu: 6-21,Fri: 6-21,Sat: 6-21,Sun: 6-21 
Mon: 6-9, Tue: 6-9,Wed: 6-9,Thu: 6-9,Fri: 6-9,Sat: 6-9,Sun: 6-9 

SQL> 
+0

Cosa succede se ci sono 1, 2 o 20 valori in un input? – MT0

+1

@ MT0 L'ho già spiegato nella mia risposta. L'espressione non è la stessa per tutte le righe, quindi è possibile utilizzare ** CASE **. Ma dimmi, quale calendario ha più di 7 giorni in una settimana? Logicamente, la domanda dell'OP sta concatenando il nome del giorno a ciascun modello nell'espressione regolare. So che potrebbe anche essere fatto usando il metodo [** generatore di righe e lo split string **] (http://lalitkumarb.com/2015/03/04/split-comma-delimited-strings-in-a-table-in -oracolo/). Ma mi sono concentrato su ** 'regexp_replace' ** come OP menzionato nella domanda. –

+0

Se ci sono 10 giorni nell'input, si desidera una settimana intera più 3 giorni aggiuntivi della settimana successiva, un punto che sembra essere mancato quando si visualizzano calendari diversi. Con questo metodo dovrai digitare un 'CASE' per ogni potenziale lunghezza di input - e se ne manchi uno allora otterrai' NULL' invece del risultato atteso. Inoltre, hai spazi bianchi prima delle virgole. – MT0

2

SQL Fiddle

Oracle 11g R2 Setup Schema:

Query 1:

with t_view (col) as (
     select '6-21 6-21 6-21 6-21 6-21 6-21 6-21 ' from dual 
union select '6-20 6-20 6-20 6-20 6-20 ' from dual 
union select '6-9 6-9 6-9 6-9 6-9 6-9 6-9 6-9 6-9' from dual 
union select '6-1' from dual 
union select '6-1 6-2' from dual 
), 
days (id, day) AS (
      SELECT 1, 'Mon' FROM DUAL 
    UNION ALL SELECT 2, 'Tue' FROM DUAL 
    UNION ALL SELECT 3, 'Wed' FROM DUAL 
    UNION ALL SELECT 4, 'Thu' FROM DUAL 
    UNION ALL SELECT 5, 'Fri' FROM DUAL 
    UNION ALL SELECT 6, 'Sat' FROM DUAL 
    UNION ALL SELECT 0, 'Sun' FROM DUAL 
), 
matches (col, idx, day) AS (
    SELECT col, 
     COLUMN_VALUE, 
     day || ': ' || REGEXP_SUBSTR(t.col, '\d+-\d+', 1, COLUMN_VALUE) 
    FROM t_view t, 
     TABLE(
      CAST(
      MULTISET(
       SELECT LEVEL 
       FROM DUAL 
       CONNECT BY LEVEL <= REGEXP_COUNT(t.col, '\d+-\d+') 
      ) 
      AS SYS.ODCINUMBERLIST 
      ) 
     ) l 
     INNER JOIN days d 
     ON (MOD(l.COLUMN_VALUE, 7) = d.id) 
) 
SELECT LISTAGG(day, ', ') WITHIN GROUP (ORDER BY IDX) AS col 
FROM matches 
GROUP BY col 

Results:

|                      COL | 
|------------------------------------------------------------------------------------------| 
|                     Mon: 6-1 | 
|                  Mon: 6-1, Tue: 6-2 | 
|         Mon: 6-20, Tue: 6-20, Wed: 6-20, Thu: 6-20, Fri: 6-20 | 
|    Mon: 6-21, Tue: 6-21, Wed: 6-21, Thu: 6-21, Fri: 6-21, Sat: 6-21, Sun: 6-21 | 
| Mon: 6-9, Tue: 6-9, Wed: 6-9, Thu: 6-9, Fri: 6-9, Sat: 6-9, Sun: 6-9, Mon: 6-9, Tue: 6-9 | 
+0

'LISTAGG' è supportato solo su' 11gR2' e su. –

+0

Esistono più [altre tecniche di aggregazione delle stringhe] (https://oracle-base.com/articles/misc/string-aggregation-techniques) che possono essere utilizzate nelle versioni precedenti (se 'LISTAGG' non è disponibile). – MT0

+0

@ MTO Dal punto di vista delle prestazioni, la vostra soluzione sarà estremamente lenta. Confronta i piani di spiegazione. –

1

Considerando Space come delimitatore, tokenise nelle file (utilizzando i livelli) e ricongiungersi con LISTAGG(), utilizzando il livello come generatore di giorno (TO_CHAR(TRUNC(SYSDATE,'D')+level)

with t_view as 
(
    select '6-21 6-21 6-21 6-21 6-21 6-21 6-21 ' as col from dual 
    union all 
    select '6-20 6-20 6-20 6-20 6-20 ' from dual 
    union all 
    select '6-9 6-9 6-9 6-9 6-9 6-9 6-9 6-9 ' from dual 
) 
SELECT LISTAGG(TO_CHAR(TRUNC(SYSDATE,'D')+level1,'Dy')||': '|| 
       REGEXP_SUBSTR(col,'[^ ]+',1,LEVEL1),', ') 
     WITHIN GROUP (ORDER BY level1) 
from 
(
SELECT col,level level1 
    FROM t_view 
CONNECT BY REGEXP_SUBSTR(col,'[^ ]+',1,LEVEL) IS NOT NULL 
    AND PRIOR col = col 
    AND PRIOR sys_guid() IS NOT NULL 
) 
group by col; 
1

Questa è la mia prova . Non è diverso dalla versione di MTo. L'ideea è la stessa: trasformare stringhe in linee, aggiungere informazioni sul giorno, quindi raggruppare il record.

with week as (
    select 1 day_num, 'Mon' day_name from dual union all 
    select 2 day_num, 'Tue' day_name from dual union all 
    select 3 day_num, 'Wed' day_name from dual union all 
    select 4 day_num, 'Thu' day_name from dual union all 
    select 5 day_num, 'Fri' day_name from dual union all 
    select 6 day_num, 'Sat' day_name from dual union all 
    select 7 day_num, 'Sun' day_name from dual 
), 
t_view as (select '6-21 6-21 6-21 6-21 6-21 6-21 6-21 ' as col from dual 
       union all 
       select '6-20 6-20 6-20 6-20 6-20 ' from dual 
       union all 
       select '6-9 6-9 6-9 6-9 6-9 6-9 6-9 ' from dual 
       ), 
lines as(
    select 
    col, WEEK.DAY_NAME, l, trim(regexp_substr(col, '[^,]+', 1, L)) elem 
    from (
     select regexp_replace(col,'([[:digit:]]-[[:digit:]]{1,2}[[:space:]]+)','\1,') col 
     from t_view 
    ) 
     join (select level l from dual connect by level < 10) 
    on instr(col, ',', 1, L) > 0 
    join week on WEEK.DAY_NUM = l 
    order by col,l 
) 
select listagg(day_name||':'||elem,' ') within group (order by l) 
from lines 
group by col; 

Risultato:

Mon:6-20 Tue:6-20 Wed:6-20 Thu:6-20 Fri:6-20 
Mon:6-21 Tue:6-21 Wed:6-21 Thu:6-21 Fri:6-21 Sat:6-21 Sun:6-21 
Mon:6-9 Tue:6-9 Wed:6-9 Thu:6-9 Fri:6-9 Sat:6-9 Sun:6-9