Il modo migliore per incapsulare la complessa logica del cursore Oracle PL/SQL come vista?

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

  •  09-06-2019
  •  | 
  •  

Domanda

Ho scritto codice PL/SQL per denormalizzare una tabella in un formato molto più semplice da interrogare.Il codice utilizza una tabella temporanea per svolgere parte del suo lavoro, unendo insieme alcune righe della tabella originale.

La logica è scritta come a funzione di tabella pipeline, seguendo lo schema dell'articolo collegato.La funzione tabella utilizza a PRAGMA AUTONOMOUS_TRANSACTION dichiarazione per consentire la manipolazione della tabella temporanea e accetta anche un parametro di input del cursore per limitare la denormalizzazione a determinati valori ID.

Ho quindi creato una vista per interrogare la funzione tabella, passando tutti i possibili valori ID come cursore (altri usi della funzione saranno più restrittivi).

La mia domanda:tutto questo è davvero necessario?Ho perso completamente un modo molto più semplice per ottenere la stessa cosa?

Ogni volta che tocco PL/SQL ho l'impressione di scrivere troppo.

Aggiornamento: Aggiungo uno schizzo della tabella con cui mi sto occupando per dare a tutti un'idea della denormalizzazione di cui sto parlando.La tabella memorizza una cronologia dei lavori dei dipendenti, ciascuno con una riga di attivazione e (possibilmente) una riga di cessazione.È possibile che un dipendente abbia più lavori simultanei, nonché lo stesso lavoro più e più volte in intervalli di date non contigui.Per esempio:

| 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 |

Interrogarlo per capire chi lavora e quando in quale lavoro non è banale.Pertanto, la mia funzione di denormalizzazione popola la tabella temporanea solo con gli intervalli di date per ciascun lavoro, per qualsiasi EMP_IDviene passato tramite il cursore.Di passaggio EMP_IDs 1 e 2 produrrebbero quanto segue:

| 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 consente NULLs per lavori che non hanno una data di fine predeterminata.)

Come puoi immaginare, questo modulo denormalizzato è molto, molto più semplice da interrogare, ma crearlo, per quanto ne so, richiede una tabella temporanea per memorizzare i risultati intermedi (ad esempio, i record di lavoro per i quali è stata impostata la riga di attivazione trovato, ma non la risoluzione...ancora).Utilizzare la funzione di tabella pipeline per popolare la tabella temporanea e quindi restituirne le righe è l'unico modo in cui ho capito come farlo.

È stato utile?

Soluzione

Penso che un modo per affrontare questo problema sia utilizzare le funzioni analitiche...

Ho impostato il tuo caso di test utilizzando:

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;

Ho usato il Guida per ottenere la data successiva e quindi racchiudere il tutto come sottoquery solo per ottenere i record "A" e aggiungere la data di fine, se ce n'è una.

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

Sono sicuro che ci sono alcuni casi d'uso che mi sono sfuggiti, ma hai capito.Le funzioni analitiche sono tue amiche :)

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                              

Altri suggerimenti

Invece di avere il parametro di input come cursore, avrei una variabile di tabella (non so se Oracle ha una cosa del genere, sono un tipo TSQL) o popolare un'altra tabella temporanea con i valori ID e unirmi ad essa nella vista /funzione o ovunque sia necessario.

Secondo la mia onesta opinione, l'unico momento per i cursori è quando tu Avere eseguire il ciclo.E quando devi eseguire il loop, consiglio sempre di farlo al di fuori del database nella logica dell'applicazione.

Sembra che tu stia dando via una certa coerenza di lettura qui, ad esempio:sarà possibile che il contenuto della tabella temporanea non sia sincronizzato con i dati di origine, se si dispone di modifiche simultanee ai dati.

Senza conoscere i requisiti, né la complessità di ciò che si vuole ottenere.Ci proverei

  1. per definire una vista, contenente logica (possibilmente complessa) in SQL, altrimenti aggiungerei un po' di PL/SQL al mix con;
  2. Una funzione di tabella in pipeline, ma che utilizza un tipo di raccolta SQL (invece della tabella temporanea).Un semplice esempio è qui: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4447489221109

Il numero 2 ti fornirebbe meno parti mobili e risolverebbe il tuo problema di coerenza.

Matteo Butler

Il vero problema qui è il design della tabella "di sola scrittura", con questo intendo che è facile inserirvi dati, ma complicato e inefficiente ottenerne informazioni utili!La tua tabella "temporanea" ha la struttura che avrebbe dovuto avere la tabella "permanente" in primo luogo.

Potresti forse fare questo:

  • Crea una tabella permanente con la struttura migliore
  • Compilarlo in modo che corrisponda ai dati nella prima tabella
  • Definire un trigger di database sulla tabella originale per mantenere sincronizzata la nuova tabella da ora in poi

Quindi puoi semplicemente selezionare dalla nuova tabella per eseguire il reporting.

Non potrei essere più d'accordo con te, HollyStyles.Ero anche un tipo da TSQL e trovavo alcune delle idiosincrasie di Oracle più che un po' sconcertanti.Sfortunatamente, le tabelle temporanee non sono così convenienti in Oracle e, in questo caso, altra logica SQL esistente si aspetta di interrogare direttamente una tabella, quindi le fornisco invece questa visualizzazione.In questo sistema non esiste davvero alcuna logica applicativa al di fuori del database.

Gli sviluppatori Oracle sembrano utilizzare i cursori con molto più entusiasmo di quanto avrei pensato.Considerata la natura vincolante e disciplinare di PL/SQL, ciò è ancora più sorprendente.

La soluzione più semplice è:

  1. Creare un tabella temporanea globale contenente solo gli ID necessari:

    CREATE GLOBAL TEMPORARY TABLE tab_ids (id INTEGER)  
    ON COMMIT DELETE ROWS;
    
  2. Compila la tabella temporanea con gli ID necessari.

  3. Utilizza l'operazione EXISTS nella procedura per selezionare le righe presenti solo nella tabella ID:

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

Puoi anche passare una stringa di ID separati da virgole come parametro di funzione e analizzarla in una tabella.Questa operazione viene eseguita da una singola SELECT.Vuoi saperne di più - chiedimi come :-) Ma deve essere una domanda a parte.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top