1

estoy trabajando en postgresql y oracle 11g, quiero asignar nombres a una columna tipo y fecha mayor, me explico:

Tengo la siguiente tabla:

| id  |   fecha    | cod   | descripcion |
|-----|------------|-------|-------------|
| 001 | 2020/01/01 | TP01  | fruta       |
| 001 | 2020/01/02 | TP0X  | comida      |
| 001 | 2020/01/03 | TP03  | verdura     |
| 001 | 2020/01/04 | TP06  | bebestible  |
| 001 | 2020/01/05 | TP0X  | licor       |
| 001 | 2020/01/05 | TP10  | legumbre    |
| 002 | 2020/01/03 | TP03  | verdura     |
| 002 | 2020/01/09 | TP0X  | comida      |
| 002 | 2020/01/08 | TP0X  | licor       |
| 002 | 2020/01/02 | TP10  | legumbre    |
|-----|------------|-------|-------------|

Lo que necesito es que el tipo TP0X cambie su descripción para el resto de las filas dependiendo de la fecha, el primer valor que encuentre después lo reemplace por el segundo hacia a delante, el resultado también es agrupado por id, quedando de esta forma:

| id  |   fecha    | cod   | descripcion | fix         |
|-----|------------|-------|-------------|-------------|
| 001 | 2020/01/01 | TP01  | fruta       | comida      |
| 001 | 2020/01/02 | TP0X  | comida      | comida      |
| 001 | 2020/01/03 | TP03  | verdura     | comida      |
| 001 | 2020/01/04 | TP06  | bebestible  | comida      |
| 001 | 2020/01/05 | TP0X  | licor       | licor       |
| 001 | 2020/01/05 | TP10  | legumbre    | licor       |
| 002 | 2020/01/02 | TP10  | legumbre    | comida      |
| 002 | 2020/01/03 | TP03  | verdura     | comida      |
| 002 | 2020/01/08 | TP0X  | comida      | comida      |
| 002 | 2020/01/09 | TP0X  | licor       | licor       |
|-----|------------|-------|-------------|-------------|

De antemano muchas gracias.

SLinerosC
  • 35
  • 5

1 Answers1

0

Mi solución se divide en varios pasos lógicos:

  1. El primer CTE llamado MiTabla simplemente simula la tabla subyacente con los datos que has dado de ejemplo.

  2. Dado que no parece haber un campo de tipo secuencial que pueda servir para ordenar de manera fácil los datos, lo primero que hago es crear al vuelo (en el primer CTE llamado MiTablaOrdenada) un campo secuencial (de 1 a N) con el orden que deben tener las filas. Mi sugerencia es, si tu tabla subyacente tiene algún campo que sirva para este efecto, utilizarlo directamente.

  3. Luego, en el CTE BaseDescripcion encuentro todas las filas que servirán para colocar la descripción a utilizar en la columna Fix, además aprovecho para encontrar el Número de fila del registro anterior y número de fila del registro siguiente. Para que el algoritmo funcione de manera independiente con cada nuevo id en la tabla subyacente hago particiones en la función de ventana por el campo id. Esto hará que el motor deje en NULL el campo FilaAnterior para cada primera aparición del valor TP0X en cada nuevo id, lo que es vital para construir la solución.

  4. Finalmente, armo la solución en el CTE AsignaFix haciendo la unión de las siguientes consultas:

    1. Obtengo las filas con cod TP0X directamente desde el CTE BaseDescripcion, asignando la descripción misma al campo Fix. Además me traigo la columna NumeroFila que servirá para ordenar el resultado final.

    2. Obtengo las filas que están después de cada fila con cod TP0X, haciendo un inner join de BaseDescripcion con MiTablaOrdenada obteniendo todas las filas que pertenecen al mismo id y están entre el NumeroFila de la fila base de la descripción y el NumeroFilaSiguiente (sin incluirlos), y asignando la descripcion obtenida de BaseDescripcion al campo Fix.

    3. Finalmente obtengo las filas que, para cada id, están antes de la primera aparición del cod TP0X, también con un inner join entre las mismas tablas, solamente ajustando la condición. De manera análoga asigno la descripcion obtenida de BaseDescripcion al campo Fix.

En código, se ve así:

with   
MiTabla as (   
select  '001' id, to_date('2020/01/01', 'yyyy/mm/dd') fecha, 'TP01' cod, 'fruta' descripcion from dual   
union all select '001', to_date('2020/01/02', 'yyyy/mm/dd'), 'TP0X', 'comida' from dual    
union all select '001', to_date('2020/01/03', 'yyyy-mm-dd'), 'TP03', 'verdura' from dual    
union all select '001', to_date('2020/01/04', 'yyyy-mm-dd'), 'TP06', 'bebestible' from dual    
union all select '001', to_date('2020/01/05', 'yyyy-mm-dd'), 'TP0X', 'licor' from dual    
union all select '001', to_date('2020/01/05', 'yyyy-mm-dd'), 'TP10', 'legumbre' from dual    
union all select '002', to_date('2020/01/03', 'yyyy-mm-dd'), 'TP03', 'verdura' from dual    
union all select '002', to_date('2020/01/08', 'yyyy-mm-dd'), 'TP0X', 'comida' from dual    
union all select '002', to_date('2020/01/09', 'yyyy-mm-dd'), 'TP0X', 'licor' from dual    
union all select '002', to_date('2020/01/02', 'yyyy-mm-dd'), 'TP10', 'legumbre' from dual   
)   
,   
MiTablaOrdenada as (   
select row_number() over (order by id, fecha, cod) as NumeroFila   
       , id   
       , fecha   
       , cod   
       , descripcion   
  from MiTabla   
)   
,   
BaseDescripcion as (   
select   lag(NumeroFila) over (partition by id order by NumeroFila) FilaAnterior   
       , NumeroFila   
       , lead(NumeroFila) over (partition by id order by NumeroFila) FilaSiguiente   
       , id   
       , fecha   
       , cod   
       , Descripcion   
  from MiTablaOrdenada   
 where Cod = 'TP0X'   
)   
,   
AsignaFix as (   
select NumeroFila, id, fecha, cod, Descripcion, Descripcion Fix   
  from BaseDescripcion   
union all   
select b.NumeroFila, b.id, b.fecha, b.cod, b.Descripcion, a.descripcion   
  from BaseDescripcion a   
       inner join MiTablaOrdenada b on b.id = a.id and b.NumeroFila > a.NumeroFila and b.NumeroFila < coalesce(a.FilaSiguiente, 999999999)   
union all   
select b.NumeroFila, b.id, b.fecha, b.cod, b.Descripcion, a.descripcion   
  from BaseDescripcion a   
       inner join MiTablaOrdenada b on b.id = a.id and b.NumeroFila < a.NumeroFila   
 where a.FilaAnterior is null   
)   
select *   
  from AsignaFix   
 order by NumeroFila

El resultado obtenido es:

NUMEROFILA ID   FECHA     COD   DESCRIPCION   FIX
=====================================================================
1          001  01-JAN-20 TP01  fruta         comida
2          001  02-JAN-20 TP0X  comida        comida
3          001  03-JAN-20 TP03  verdura       comida
4          001  04-JAN-20 TP06  bebestible    comida
5          001  05-JAN-20 TP0X  licor         licor
6          001  05-JAN-20 TP10  legumbre      licor
7          002  02-JAN-20 TP10  legumbre      comida
8          002  03-JAN-20 TP03  verdura       comida
9          002  08-JAN-20 TP0X  comida        comida
10         002  09-JAN-20 TP0X  licor         licor

Puedes probar la solución en el sitio de Oracle en vivo.

Aviso de edición

La respuesta ha sido adaptada a raíz de que la pregunta fue editada por el AP. La edición incluye el caso de contar con diversos id's en los datos de entrada y que la lógica explicada por el AP aplica de manera independiente para cada id.

La solución original se encuentra aún disponible en oracle live.

jachguate
  • 25,659
  • 7
  • 35
  • 61
  • me sirvió harto tu código e explicación, pero me surgió un problema, cuando tengo un valor distinto en la columna id, el resultado se desordena, le di muchas vueltas a tu código pero no logré solucionarlo. Al agregar: `union all select '002', to_date('2020/01/03', 'yyyy-mm-dd'), 'TP03', 'verdura' from dual union all select '002', to_date('2020/01/09', 'yyyy-mm-dd'), 'TP0X', 'comida' from dual union all select '002', to_date('2020/01/08', 'yyyy-mm-dd'), 'TP0X', 'licor' from dual union all select '002', to_date('2020/01/02', 'yyyy-mm-dd'), 'TP10', 'legumbre' from dual` – SLinerosC Jul 08 '20 at 16:33
  • ¿Qué significa que se desordena? El computador siempre va a respetar el orden solicitado en las cláusulas `order by` – jachguate Jul 09 '20 at 08:00
  • talvez me expresé mal, edité la pregunta principal con el ejemplo para tener mas claridad, pasa que el resultado no los agrupa por id, cuando consulto por un caso en particular por id me resulta bien, pero al agregar un id distinto el resultado no se agrupa por id, no se si lo logro explicar bien, el id es el que manda o agrupa los resultados, Gracias. – SLinerosC Jul 09 '20 at 14:58
  • Ya veo, luego de tu edición cual es el tema. Pienso que si comprendieras la lógica de mi respuesta, podrías adaptar el script fácilmente para soportar el caso que se te está presentando y, de verdad, no me gustaría terminar haciendo tu tarea o tu trabajo, pues si bien estamos para ayudar, la idea es ayudar a aprender, no a salir de los compromisos de cada uno, que ya son cosas distintas. Por lo mismo, dejaré mi respuesta tal como está. Si demuestras que te esfuerzas por hacerlo por ti mismo, seguiré por acá si te surge alguna duda. – jachguate Jul 09 '20 at 15:04
  • @SLinerosC, al final edité mi pregunta, solo por el hecho que había que incluir la cláusula `partition by` que probablemente no conocías aún. Dejo mi comentario anterior, porque creo que sigue siendo válido el pedir que te esfuerces por comprender la solución y no simplemente aplicarla. – jachguate Jul 09 '20 at 15:23