La mejor manera de resumir el complejo de Oracle PL/SQL cursor de la lógica como un punto de vista?

StackOverflow https://stackoverflow.com/questions/20081

  •  09-06-2019
  •  | 
  •  

Pregunta

He escrito el código PL/SQL para denormalize una tabla en un mucho-de la pascua-a-formulario de consulta.El código utiliza una tabla temporal para hacer parte de su trabajo, la fusión de algunas filas de la tabla original juntos.

La lógica es escrito como un canalizado función de tabla, siguiendo el patrón por el artículo vinculado.La tabla de la función utiliza un PRAGMA AUTONOMOUS_TRANSACTION declaración de permiso temporal de la manipulación de la tabla, y también acepta un cursor de parámetro de entrada para restringir la desnormalización para ciertos valores de ID.

Luego he creado una vista a la consulta de la tabla de la función, pasando en todos los posibles valores de ID como un cursor (otros usos de la función será la más restrictiva).

Mi pregunta:es todo esto realmente necesario?He perdido por completo una forma mucho más sencilla forma de llevar a cabo la misma cosa?

Cada vez que me toque PL/SQL me da la impresión de que estoy escribiendo demasiado.

Actualización: Voy a añadir un boceto de la tabla que estoy tratando de dar a cada uno una idea de la desnormalización de la que estoy hablando.La tabla almacena un historial de puestos de trabajo de los empleados, cada uno con una activación de la fila, y (posiblemente) una terminación de la fila.Es posible que un empleado tenga múltiples puestos de trabajo, así como el mismo trabajo una y otra vez en no contiguos rangos de fecha.Por ejemplo:

| EMP_ID | JOB_ID | STATUS | EFF_DATE    | other columns...
|      1 |     10 | A      | 10-JAN-2008 |
|      2 |     11 | A      | 13-JAN-2008 |
|      1 |     12 | A      | 20-JAN-2008 |
|      2 |     11 | T      | 01-FEB-2008 |
|      1 |     10 | T      | 02-FEB-2008 |
|      2 |     11 | A      | 20-FEB-2008 |

La consulta que averiguar quién está trabajando cuando en lo que trabajo no es trivial.Por lo tanto, mi desnormalización función rellena la tabla temporal con sólo la fecha de los intervalos para cada puesto de trabajo, por cualquier EMP_IDs pasado en aunque el cursor.Pasando EMP_IDs 1 y 2 se iba a producir la siguiente:

| EMP_ID | JOB_ID | START_DATE  | END_DATE    |
|      1 |     10 | 10-JAN-2008 | 02-FEB-2008 |
|      2 |     11 | 13-JAN-2008 | 01-FEB-2008 |
|      1 |     12 | 20-JAN-2008 |             |
|      2 |     11 | 20-FEB-2008 |             |

(END_DATE permite NULLs para trabajos que no tienen definido su fecha de terminación.)

Como se puede imaginar, esto no normalizada forma es mucho, mucho más fácil a la consulta, sino que se crea, así que lo que puedo decir-requiere una tabla temporal para almacenar los resultados intermedios (por ejemplo, registros de trabajos para los que la activación de la fila ha sido encontrado, pero no de la terminación...aún).El uso de la canalizado función de tabla para rellenar la tabla temporal y luego regresar a sus filas es la única manera que he encontrado la manera de hacerlo.

¿Fue útil?

Solución

Creo que una manera de abordar esto es el uso de funciones analíticas...

Puedo configurar su caso de prueba utilizando:

create table employee_job (
    emp_id integer,
    job_id integer,
    status varchar2(1 char),
    eff_date date
    );  

insert into employee_job values (1,10,'A',to_date('10-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'A',to_date('13-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (1,12,'A',to_date('20-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'T',to_date('01-FEB-2008','DD-MON-YYYY'));
insert into employee_job values (1,10,'T',to_date('02-FEB-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'A',to_date('20-FEB-2008','DD-MON-YYYY'));

commit;

He usado el plomo función para obtener la siguiente fecha y, a continuación, envuelto todo esto como una sub-consulta para obtener los registros "a" y agregar la fecha de finalización si es que hay uno.

select
    emp_id,
    job_id,
    eff_date start_date,
    decode(next_status,'T',next_eff_date,null) end_date
from
    (
    select
        emp_id,
        job_id,
        eff_date,
        status,
        lead(eff_date,1,null) over (partition by emp_id, job_id order by eff_date, status) next_eff_date,
        lead(status,1,null) over (partition by emp_id, job_id order by eff_date, status) next_status
    from
        employee_job
    )
where
    status = 'A'
order by
    start_date,
    emp_id,
    job_id

Estoy seguro de que hay algunos casos de uso que se me haya olvidado, pero usted consigue la idea.Funciones analíticas son tus amigos :)

EMP_ID   JOB_ID     START_DATE     END_DATE            
  1        10       10-JAN-2008    02-FEB-2008         
  2        11       13-JAN-2008    01-FEB-2008         
  2        11       20-FEB-2008                              
  1        12       20-JAN-2008                              

Otros consejos

En lugar de tener el parámetro de entrada como un cursor, me gustaría tener una variable de tabla (no sé si el Oráculo ha dicho una cosa estoy un TSQL chico) o rellenar otra tabla temporal con los valores de ID y unirse a él en la vista de función/o dondequiera que usted necesite.

La única vez para los cursores en mi opinión es cuando han a bucle.Y cuando usted tiene un bucle siempre recomiendo a hacer que fuera de la base de datos en la lógica de la aplicación.

Suena como que usted está regalando algunos de coherencia de lectura aquí, es decir:será posible que el contenido de la tabla temporal a estar fuera de sincronización con la fuente de datos, si tiene concurrente de modificación de modificación de datos.

Sin saber los requisitos, ni la complejidad de lo que se quiere lograr.Me gustaría intentar

  1. para definir una vista que contenga (posiblemente complejas) lógica de SQL, otra cosa que me gustaría añadir algunos PL/SQL para la mezcla con;
  2. Un canalizadas función de tabla, pero el uso de una colección de SQL tipo (en lugar de la tabla temporal ).Un ejemplo sencillo es aquí: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4447489221109

Número 2 le daría menos partes móviles y resolver su consistencia problema.

Mathew Butler

El verdadero problema aquí es el "escribir" solo diseño de la tabla - con lo que quiero decir, es fácil insertar datos en ella, pero complicado e ineficiente para obtener información útil de ella!Su "temporal" de la tabla tiene la estructura de la "permanente" de la tabla debería de haber tenido en primer lugar.

Podría tal vez hacer esto:

  • Crear una tabla permanente con la mejor estructura
  • Rellenar para que coincida con los datos de la primera tabla
  • Definir una base de datos de trigger en la tabla original, para mantener la nueva tabla en la sincronización a partir de ahora

A continuación, puede seleccionar de la nueva tabla para realizar sus informes.

Yo no podía estar más de acuerdo, HollyStyles.Yo también solía ser un TSQL chico, y encontrar algunas de Oracle la idiosincrasia más que un poco desconcertante.Por desgracia, las tablas temporales no son tan convenientes en Oracle, y en este caso, a otras ya existentes en la lógica SQL está a la espera de una consulta directamente a una tabla, así que le doy esta opinión en su lugar.Realmente no hay una lógica de la aplicación que existe fuera de la base de datos en este sistema.

Oracle desarrolladores parecen utilizar los cursores mucho más entusiasmo de lo que yo hubiera pensado.Dada la esclavitud y la disciplina de la naturaleza de PL/SQL, que es la más sorprendente.

La solución más simple es:

  1. Crear un tabla temporal global solo contiene los Identificadores de que usted necesita:

    CREATE GLOBAL TEMPORARY TABLE tab_ids (id INTEGER)  
    ON COMMIT DELETE ROWS;
    
  2. Rellenar la tabla temporal con los Identificadores que usted necesita.

  3. Utilice EXISTE operación en el procedimiento para seleccionar las filas que están sólo en el Id de la tabla:

      SELECT yt.col1, yt.col2 FROM your\_table yt  
       WHERE EXISTS (  
          SELECT 'X' FROM tab_ids ti  
           WHERE ti.id = yt.id  
       )
    

También puede pasar una cadena separada por comas de los Identificadores como un parámetro de la función y analizar en una tabla.Esto es realizado por un solo SELECCIONAR.Quiero saber más - me pregunta cómo :-) Pero es que tiene que ser una cuestión separada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top