|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: Jan 2003
Città: Roma
Messaggi: 1040
|
[sql] DB Oracle: devo usare istruzioni PL/SQL?
Innanzi tutto.. non sono affatto un esperto di sql, e ancor meno di Oracle, sono però stato buttato nella mischia e devo cercare di cavarmela.
![]() La situazione è questa.. ho una tabella di questo tipo: Chiave primaria composta da: ( in maiuscolo il nome dei campi) - un numero di utenza : UTZ E - un numero, molto più piccolo, progressivo. Ogni record ha due date associate che ne indicano l'intervallo di validità, DAT_DAL e DAT_AL ( quindi sia scadute che attive, con validità fino al 2099 ), uno status STATUS( attivo o cessato, da cui dipendono chiaramente le due date) ed altri dati al momento non necessari. Insomma, ogni utente può avere diversi record, grazie al progressivo, ma può averne uno solo attivo e svariati cessati. Le possibilità sono: - tutti cessati - uno attivo - uno attivo con uno/più record cessati Tra i miei vari obiettivi, c'è quello di andare a recuperare, per gli utenti con un record attivo, il record attivo ma anche quello scaduto più di recente (SE posseduto) e non basandomi sul progressivo che invece potrebbe essere fuorviante per problemi nel sistema. Il problema è, direi oltre le mie conoscenze di sql e di oracle, e l'unica idea che mi era venuta è quella di affrontarlo diciamo più dal punto di vista della programmazione classica, appoggiandomi a dei costrutti di loop e/o if-then-else di pl/sql, che ho sbirciato sulla rete. La domanda quindi è.. è possibile fare quello che mi è richiesto, solo con comandi sql? L'uso dei costrutti loop e if, è conveniente? Sono performanti? Al momento stavo pensando di cercare ordinare per data e poi prendere la prima riga ( tra le cessate chiaramente), in modo di andare a prendere la cessata più recente, ma farlo per tutte le utenze interessante è problematico.. ![]() Scusate per la lunghezza ma dovevo cercare di essere chiaro! ![]()
__________________
Weldzar Ultima modifica di yssj485 : 11-06-2010 alle 20:42. |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Mar 2001
Città: PV Milano Nord
Messaggi: 3851
|
se posti delle righe di esempio magari ci è + facile...
come linee guida farei: tutti gli attivi select utente from tabella where stato='attivo' quello scaduto per ultimo tra quelli attivi select utente,max(datascadenza) from tabella where utente in (select utente from tabella where stato='attivo') and status<>'attivo' ma chiaramente sono solo idee base che vanno adattate al tuo contesto ![]()
__________________
"W la foca, che dio la benedoca"
poteva risolvere tutto la sinistra negli anni in cui ha governato e non l'ha fatto. O sono incapaci o sta bene anche a "loro" cosi. L'una o l'altra inutile scandalizzarsi.[plutus] |
![]() |
![]() |
![]() |
#3 |
Bannato
Iscritto dal: Apr 2006
Messaggi: 5857
|
Questa query dovrebbe fare quello che chiedi
Codice:
;WITH Attivi AS ( SELECT UTZ, PROG, DAT_DAL, DAT_AL, STATUS FROM TuaTabella WHERE STATUS = 1 --Supponendo che il campo sia di tipo Bit quindi 0/1 ), Filtro AS ( SELECT UTZ, MAX(DAT_AL) AS ULTIMO_DAT_AL FROM TuaTabella WHERE STATUS = 0 --Supponendo che il campo sia di tipo Bit quindi 0/1 GROUP BY UTZ ), Disattivi AS ( SELECT UTZ, PROG, DAT_DAL, DAT_AL, STATUS FROM TuaTabella T1 INNER JOIN Filtro T2 ON T1.UTZ = T2.UTZ AND T1.DAT_AL = T2.ULTIMO_DAT_AL ) SELECT A.*, D.* FROM Attivi A LEFT OUTER JOIN Disattivi D ON A.UTZ = B.UTZ Note:
Ciao Ultima modifica di FabryHw : 12-06-2010 alle 00:05. |
![]() |
![]() |
![]() |
#4 | |
Senior Member
Iscritto dal: Jan 2003
Città: Roma
Messaggi: 1040
|
Innanzi tutto, un ringraziamento ad entrambi
![]() Adesso sto installando Oracle a casa e farò qualche prova con i vostri consigli.. in effetti non avevo pensato all'utilizzo della funziona max per una data. Si vede che sono a digiuno di sql! ![]() Buono pure il consiglio per le Common Expression Table, in effetti sembrano molto utili per la leggibilità e, a guardare in giro, sembra siano supportate da Oracle. Quote:
Indubbiamente mi basterebbe fare due diverse query, una per gli attivi, ed una per gli scaduti più recenti.. ma, una curiosità, è possibile scorrere una tabella e fare per ogni riga due operazioni? In questo caso prendere i primi campi per creare ed inserire un record "attivo", ed i rimanenti per un record "scaduto". Grazie ancora ps. se mi date qualche consiglio su cose che potrei studiarmi/impararmi, del tipo delle CTE, PL/sql, o materiale online, fareste una cosa graditissima ![]()
__________________
Weldzar Ultima modifica di yssj485 : 12-06-2010 alle 13:26. |
|
![]() |
![]() |
![]() |
#5 | |
Senior Member
Iscritto dal: Jan 2003
Città: Roma
Messaggi: 1040
|
Quote:
Ho tradotto in codice quello che dicevi.. Diciamo che i record che andrebbe a prendere sono giusti, il problema è che, come dicevo prima, io devo creare una tabella con un sottoinsieme dei record della tabella principale, quindi devo prendere tutti i campi. In questo caso, nella select esterna vado a raccogliere sì i record giusti, ma non mi faccio restituire la chiave primaria(composta anche dal numero progressivo, oltre che dall'utenza) e quindi non posso andare a recuperare tutti i dati dei record in maniera "pulitissima". Ad esempio, ggiungendo una query che confronta num_utz e dat_al, sembra che vada, e non credo che dovrei avere problemi, ma non usare la seconda parte della chiave per identificare il record, direi che non mi piace troppo.. che dici? Prestazionalmente poi come credi che vada? Considera che dovrò buttarla su una tabella con almeno un milione di record.. Ecco il codice ( ho usato subito le CTE): Codice:
WITH SCADUTI AS( SELECT NUM_UTZ, MAX(DAT_AL) AS DAT_AL FROM TABELLA wHERE NUM_UTZ IN (SELECT NUM_UTZ FROM TABELLA WHERE COD_STAT = 'attivo' ) AND COD_STAT = 'scaduto' GROUP BY NUM_UTZ ) SELECT D.* FROM TABELLA T INNER JOIN SCADUTI S ON T.NUM_UTZ = S.NUM_UTZ WHERE T.DAT_AL = S.DAT_AL;
__________________
Weldzar Ultima modifica di yssj485 : 13-06-2010 alle 19:07. |
|
![]() |
![]() |
![]() |
#6 |
Senior Member
Iscritto dal: Jan 2003
Città: Roma
Messaggi: 1040
|
Ho modificato ora qualcosina della tua, nella SELECT finale, per ottenere quello che mi serve.. il problema ora è capire tra le due, quella che dovrebbe essere più veloce.. mi piacerebbe capire meglio la questione delle performance e dei costrutti preferibili da questo punto di vista.
__________________
Weldzar |
![]() |
![]() |
![]() |
#7 | |
Bannato
Iscritto dal: Apr 2006
Messaggi: 5857
|
Quote:
Non ti faccio vedere un esempio di cursore perché probabilmente la sintassi Sql Server è diversa da quella Oracle. Poi elaborando un record alla volta puoi fare tutte le operazioni che vuoi, cmq nel tuo caso non sarebbero lo stesso 2 operazioni sullo stesso record (riga) dato che record attivo e ultimo record scaduto sono appunto 2 record. Cmq su un DB relazionale il codice procedurale dovresti evitarlo il più possibile e non usarlo mai quando ne puoi fare a meno. Esempio questo codice dovrebbe fare al caso tuo ma senza parsare 1 record alla volta: Codice:
;WITH Lista AS ( SELECT UTZ, PROG, DAT_DAL, DAT_AL, STATUS, ROW_NUMBER() OVER (PARTITION BY UTZ ORDER BY DAT_AL DESC) AS ROW_ID FROM TuaTabella T1 ) SELECT L.UTZ, L.PROG, L.DAT_DAL, L.DAT_AL, L.STATUS FROM Lista L WHERE ROW_ID <= 2 Di conseguenza con ID 1 avrai tutti i record attivi e con ID da 2 in poi i record disattivi in ordine dal più recente al più vecchio. Filtrando per tenere solo gli id 1 e 2 ottieni proprio quello che volevi tu, ossia per ogni utenza ti dà 1 record attivo e l'eventuale record disattivo. Row_Number() è una delle funzioni di rango introdotte con Sql Server 2005, non so se Oracle le ha, ma supporrei di si. Ultima modifica di FabryHw : 13-06-2010 alle 22:41. |
|
![]() |
![]() |
![]() |
#8 | |
Bannato
Iscritto dal: Apr 2006
Messaggi: 5857
|
Quote:
Basta pensare che la stessa cosa la puoi fare con un procedurale che looppa che con una query per vedere che hai già 2 soluzioni possibili. Poi quale sia tra le N soluzioni la più performance dipende da molte variabili (indici sui campi che sono interessati, bontà del codice che scrivi, ...ecc). Di solito sta all'abilità del programmatore scegliere la soluzione migliore o il migliore compromesso (es. metterci 3 giorni a fare un codice che poi è solo il 30% più veloce di un codice che potevi fare in 4 ore, è forse uno spreco). Prova più soluzioni e poi studiati il piano di esecuzione del DB per vedere quando è efficiente il codice. Ciao PS Se hai tabelle con milioni di record, l'approccio procedurale un record alla volta è un suicidio per il DB ed impiegherà tempi notevoli (dell'ordine di minuti o anche ore a seconda della potenza della macchina). Ultima modifica di FabryHw : 13-06-2010 alle 11:40. |
|
![]() |
![]() |
![]() |
#9 | |
Senior Member
Iscritto dal: Mar 2001
Città: PV Milano Nord
Messaggi: 3851
|
Quote:
![]() soprattutto quella di provare varie soluzioni per poi implementare quella che secondo te è corretta! aggiungerei anche che dovresti sempre tenere conto di eventuali sviluppi futuri... se per esempio questa query dovesse essere adattata a diverse situazioni, oppure ampliata per nuove richieste è meglio cercare di scrivere qualcosa di customizzabile... insomma noi possiamo darti delle linee guida (e mi pare che hai già da lavorare abbastanza ![]() ![]() metti che devi lanciare le estrazioni una tantum e di notte, si potrebbe optare per una soluzione magari + macchinosa ma + customizzabile appunto. se invece dovessi estrarre quelle info magari al volo e in qualsiasi momento, beh la snellezza prevale! insomma...devi capire bene tu ![]()
__________________
"W la foca, che dio la benedoca"
poteva risolvere tutto la sinistra negli anni in cui ha governato e non l'ha fatto. O sono incapaci o sta bene anche a "loro" cosi. L'una o l'altra inutile scandalizzarsi.[plutus] |
|
![]() |
![]() |
![]() |
#10 |
Senior Member
Iscritto dal: Jan 2003
Città: Roma
Messaggi: 1040
|
Beh ragazzi, vi ringrazio nuovamente, e posso dire di aver imparato più cose chiacchierando con voi e studiandomi le cose di cui avete parlato, che riguardandomi il libro di Basi di Dati
![]() Il discorso delle prestazioni è più che chiaro, però vorrei capire se ci sono occasioni in cui è meglio utilizzare una certa struttura piuttosto che un'altra.. non esistono, vi chiedo da perfetto ignorante, software che valutano l'efficienza di una query in base al Database considerato? Ho paura che mi toccherà comprare qualche libro di Oracle per capirci qualcosa. Per quanto riguarda il nuovo esempio di FabryHw.. proprio ora ero rimasto incuriosito dal Partition By, che pensavo sarebbe potuto essere utile, e te me lo hai presentato al volo, grande ![]()
__________________
Weldzar |
![]() |
![]() |
![]() |
#11 |
Senior Member
Iscritto dal: Jan 2003
Città: Roma
Messaggi: 1040
|
Una domanda diciamo più o meno sulle basi.. approfitto del topic.
Su Oracle, ma credo sia una questione generale, c'è una differenza prestazionale immensa tra un Join, a TRE TABELLE ( molto ricche di record )del tipo FROM Tabella1 T1, Tabella2 T2, TABELLA 3 WHERE T1.cod = T2.cod AND T2.cod2 = T3.cod2 ed un inner join FROM Tabella 1 T1 INNER JOIN Tabella2 T2 ON T1.cod = T2.cod INNER JOIN Tabella3 T3 ON T2.cod2 = T3.cod2 Perchè? Su Oracle, a quanto ho letto, nel primo caso viene eseguito un equijoin sul campo specificato nel where, mentre nel secondo caso viene effettuata chiaramente una inner join.. il risultato è che la prima query è, senza esagerare, centinaia di volte più lenta della seconda, che in alcuni casi è quasi immediata. Che differenza c'è, a basso livello e come operazioni che fa il database? Ne sapete qualcosa? Grazie
__________________
Weldzar |
![]() |
![]() |
![]() |
#12 |
Member
Iscritto dal: Aug 2005
Messaggi: 168
|
Visto che un equijoin è solo un sottotipo di inner join mi sembra strano
![]() A meno che in qualche modo non sia stato disabilitato l'ottimizzatore di Oracle e quindi lui prima faccia il prodotto cartesiano e poi controlla i where ma mi sembrerebbe incredibilmente strano, non so neanche se è possibile ![]() Magari a qualcun'altro viene un'idea. EDIT: Ma dove l'hai letto? |
![]() |
![]() |
![]() |
#13 | |
Bannato
Iscritto dal: Apr 2006
Messaggi: 5857
|
Quote:
Vedi : http://it.wikipedia.org/wiki/Join_%28SQL%29 Inoltre la tua prima sintassi non è altro che il VECCHIO metodo per scrivere le INNER JOIN, prima che implementassero delle keyword apposite. Tra l'altro puoi pure portare nella join condizioni WHERE esterne alla relazione di join tra le tabelle. Esempio il codice Codice:
FROM Tabella1 T1 INNER JOIN Tabella2 T2 ON T1.id = T2.id WHERE T1.cod = 10 Codice:
FROM Tabella1 T1 INNER JOIN Tabella2 T2 ON T1.id = T2.id AND T1.cod = 10 Perché Oracle non esegua il primo codice in modo efficiente non lo so, prova a confrontare i piani di esecuzione e vedi cosa cambia. Ultima modifica di FabryHw : 15-06-2010 alle 23:53. |
|
![]() |
![]() |
![]() |
#14 |
Senior Member
Iscritto dal: Jan 2003
Città: Roma
Messaggi: 1040
|
Beh su una guida ad Oracle di html.it, l'elencare le tabelle sul FROM dice che sia trattato come un normale Equijoin. Il fatto che siano così diversi prestazionalmente infatti non mi quadra moltissimo ( parlo di 4 sec contro 430 secondi..).
Provo allora a vedere se da qualche parte riesco ad ottenere qualche informazione in più su come si comporti realmente il DB, soprattutto in casi con join tra tre più o più tabelle.
__________________
Weldzar |
![]() |
![]() |
![]() |
#15 | |
Senior Member
Iscritto dal: Jul 2005
Città: Bologna
Messaggi: 1130
|
Quote:
Hai messo gli indici sulle FK?
__________________
-> The Motherfucking Manifesto For Programming, Motherfuckers |
|
![]() |
![]() |
![]() |
#16 |
Member
Iscritto dal: Aug 2005
Messaggi: 168
|
Nel caso non lo sapessi per vedere l'execution plan devi usare il comando
Codice:
EXPLAIN PLAN FOR Se lo sapevi già +1 :P |
![]() |
![]() |
![]() |
#17 |
Senior Member
Iscritto dal: Jan 2003
Città: Roma
Messaggi: 1040
|
Ok, ci ho provato, ma questo è il risultato.
EXPLAIN PLAN FOR SELECT * FROM FATTURA F INNER JOIN ESITO_FATTURA E ON F.COD_ESIFAT = E.COD_ESIFAT INNER JOIN ENTE_ESATTORE ES ON F.COD_ESATTORE = ES.COD_ENTE Report errori: Errore SQL: ORA-00604: error occurred at recursive SQL level 1 ORA-01552: cannot use system rollback segment for non-system tablespace 'CONTAB_DATI' 00604. 00000 - "error occurred at recursive SQL level %s" *Cause: An error occurred while processing a recursive SQL statement (a statement applying to internal dictionary tables). *Action: If the situation described in the next error on the stack can be corrected, do so; otherwise contact Oracle Support. Immagino di non avere qualche permesso o qualcosa del genere(chiaramente è un DB remoto)..
__________________
Weldzar |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 15:44.