Oracle 11g dividir la columna de texto en filas

Tengo mesa:

ID   |Values
-----+--------------------------------
1    |AB,AD
2    |AG, ... ,BD
3    |AV

¿Cómo puedo transformarlo en:

ID   |Value
-----+------
1    |AB
1    |AD
2    |AG
...  |...
2    |BD
3    |AV

preguntado el 20 de septiembre de 11 a las 06:09

Tengo que hacerlo usando una declaración de selección:

Horrible diseño de base de datos, almacenando los valores en una cadena delimitada. Recomiende a su diseñador de aplicaciones (o DBA) que dividan la cadena ANTES de que se inserte en la base de datos. -

@Ollie y votantes negativos: esta no es una situación tan infrecuente. De hecho, es una buena pregunta:

@LukasEder, puede que sea una buena pregunta, pero sigue siendo un diseño de base de datos horrible. -

No juzgaría la desnormalización antes de ver la imagen completa. Tiene su razón de ser en ciertos casos ... -

2 Respuestas

Usando las funciones XML integradas, puede hacerlo así:

with sample_data as
(
 select 1 id, 'AB,AD' vals from dual union all
 select 2, 'AG,AK,AJ,BA,BD' from dual union all
 select 3, 'AV' from dual
)
select id, cast(t.column_value.extract('//text()') as varchar2(10)) val
  from sample_data,
  table( xmlsequence( xmltype(
    '<x><x>' || replace(vals, ',', '</x><x>') || '</x></x>'
  ).extract('//x/*'))) t;

Resultado:

ID   VAL
--- -----
 1   AB
 1   AD
 2   AG
 2   AK
 2   AJ
 2   BA
 2   BD
 3   AV

Respondido el 20 de Septiembre de 11 a las 11:09

Al usar una expresión de tabla común recursiva, la misma consulta se ve así:

with sample_data as
(
  select 1 id, 'AB,AD' vals from dual union all
  select 2, 'AG,AK,AJ,BA,BD' from dual union all
  select 3, 'AV' from dual
),
split_first(id, val, rem) as
(
  select id,
    coalesce(substr(vals, 1, instr(vals, ',') - 1), vals) val,
    case when instr(vals, ',') > 0 then substr(vals, instr(vals, ',') + 1) end rem
  from sample_data
  union all
  select id,
    coalesce(substr(rem, 1, instr(rem, ',') - 1), rem) val,
    case when instr(rem, ',') > 0 then substr(rem, instr(rem, ',') + 1) end rem
  from split_first
  where rem is not null
)
select id, val from split_first
order by id;

O un enfoque ligeramente diferente:

with sample_data as
(
  select 1 id, 'AB,AD' vals from dual union all
  select 2, 'AG,AK,AJ,BA,BD' from dual union all
  select 3, 'AV' from dual
),
pos(id, seq, vals, sta, stp) as
(
  select id, 1, vals, 1, instr(vals, ',') from sample_data
  union all
  select id, seq + 1, vals, stp + 1, instr(vals, ',', stp + 1) from pos
  where stp > 0
)
select id, substr(vals, sta, case when stp > 0 then stp - sta else length(vals) end) from pos
order by id, seq;

Respondido el 20 de Septiembre de 11 a las 12:09

¿Qué solución es más eficiente? - aguja

En cuanto a la E / S, no debería haber ninguna diferencia ya que se procesan las mismas filas. En cuanto a la CPU, la segunda consulta es probablemente más eficiente ya que usa menos manipulación de cadenas. - Codo

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.