Velocizzare query incrociate basate sulla stessa tabella

di il
13 risposte

Velocizzare query incrociate basate sulla stessa tabella

Buongiorno!
Ho una tabella (collegata odbc) con un listino prezzi [TAB_PREZZI]. Ho necessità di estrarre tutti i listini variati in un determinato periodo e poi, per ciascun record di tale estrazione, aggiungere i valori del prezzo attuale, di quello precedente, eventualmente di quello successivo (che entrerà in vigore in una data successiva al periodo in analisi)
Non ho problemi concettuali a sviluppare le query, ma ad un certo punto ho la query riepilogativa che non è fruibile, impiega mezz'ora per essere eseguita.

Query che estrae i listini variati in un periodo ("010 elenco art forn"):
SELECT OFQFOR, OFQART
FROM TAB_PREZZI
WHERE (OFQDTI Between #6/1/2017# And #7/19/2017#)
GROUP BY OFQFOR, OFQART;
Impiega circa un secondo, anche meno
[dove OFQFOR, OFQART rispettivamente fornitore e articolo]

Query che estrae le date più recenti dei listini attivi ad oggi (query "020 01 list aperti"):
SELECT OFQFOR, OFQART, Max(OFQDT1) AS MaxDiOFQDT1
FROM TAB_PREZZI
WHERE (((OFQDT1)<=#7/19/2017#) AND ((OFQSTS)<>"4"))
GROUP BY OFQFOR, OFQART;
Questa query è solo propedeutica ad estrarre i valori dei prezzi attuali, con la query "020 02 list ultimi":
[dove OFQDT1 data inizio validità listino]

SELECT [020 01 list aperti].OFQFOR, [020 01 list aperti].OFQART, [020 01 list aperti].MaxDiOFQDT1, OFQVAL, OFQQUO
FROM [020 01 list aperti] INNER JOIN ASV1_OFQFI ON ([020 01 list aperti].MaxDiOFQDT1 = TAB_PREZZI.OFQDT1) AND ([020 01 list aperti].OFQART = TAB_PREZZI.OFQART) AND ([020 01 list aperti].OFQFOR = TAB_PREZZI.OFQFOR)
WHERE (((TAB_PREZZI.OFQSTS)<>"4"));
Anche qs query (basata sulla "020 01 list aperti") è veloce, meno di un secondo.
[dove OFQVAL, OFQQUO rispettivamente valuta e prezzo]

Il problema ce l'ho volendo collegare la prima query ""010 elenco art forn"" con quest'ultima...l'elaborazione diventa interminabile:

SELECT [010 elenco art forn].OFQFOR, [010 elenco art forn].OFQART, [020 02 list ultimi].MaxDiOFQDT1, [020 02 list ultimi].OFQVAL, [020 02 list ultimi].OFQQUO
FROM [010 elenco art forn] LEFT JOIN [020 02 list ultimi] ON ([010 elenco art forn].OFQART = [020 02 list ultimi].OFQART) AND ([010 elenco art forn].OFQFOR = [020 02 list ultimi].OFQFOR);

Non è la prima volta che faccio query del genere ma stavolta non riesco a risolvere

13 Risposte

  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Essendo ODBC, mi aspetto un RDBMS, di conseguenza un deve essere veloce... ma... ci sono accorgimenti che serve avere oltre a verificare con il Manager o Profiler(se è SQLMANGAER) che le query passate al Server vengano correttamente risolte dal SERVER, perchè se crei una Query parametrica che il Server non risolve, restituisce in locale tutto il RS vanificando completamente la potenza del motore del DB.
    Un esempio sono le Date... nelle Query che hai scritto non verrebbero mai risolte ServerSide in quanto SQL SERVER #....# non sa cosa sia per una data.
    Tieni poi aperta una connessione al Server nel Pool delle connessioni in modo da non doverla rigenerare ogni volta...?
    Quando si lavora in Client-Server con Access, si tiene aperto un RS su una Tabella Fittizia o vuota ServerSide in modo che nel POOl delle conessioni 1 rimanga aperta, in quel modo l'interrogazione non deve ristabilire ogni volta la connessione, ma accede al Pool e ne trova già 1... usandola, in questo modo soprattutto su Query ONESHOT si guadagna dalle 5÷10 volte come tempo di esecuzione...

    Ovviamente diamo per scontato che tu abbia INDICIZZATO i campi oggetto di Parametri/Ordinamento e Join...
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Le proprietà delle tabelle non le posso modificare, non sono l'amministratore ma solo un client. Pertanto non posso aggiungere indici alle tabelle di origine. Lato server non posso agire. Il mio campo d'azione è lato client.
    La risoluzione delle date nella sintassi indicata non mi ha mai dato problemi. Nella tabella di origine la data è un campo Data/Ora, e accetta la sintassi #GG/MM/AAAA# senza problemi, la utilizzo da anni.

    Se può essere utile per capire come potrei risolvere/migliorare posso aggiungere che creando una join inversa (right join anziché left join) sulla query finale il problema della velocità non esiste (la query è eseguita immediatamente in meno di 1sec), ma ovviamente non ottengo il risultato di records che mi serve.

    SELECT [010 elenco art forn].OFQFOR, [010 elenco art forn].OFQART, [020 02 list ultimi].OFQDT1 , [020 02 list ultimi].OFQVAL, [020 02 list ultimi].OFQQUO
    FROM [010 elenco art forn] RIGHT JOIN [020 02 list ultimi] ON ([010 elenco art forn].OFQART = [020 02 list ultimi].OFQART) AND ([010 elenco art forn].OFQFOR = [020 02 list ultimi].OFQFOR);
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Ho controllato il Pool di connessioni e risultano tutte attive con permanenza nel pool impostata a 60 sec
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Ho comunque controllato gli indici esistenti sui campi , e risultano già indicizzati

  • Re: Velocizzare query incrociate basate sulla stessa tabella

    cla68 ha scritto:


    Le proprietà delle tabelle non le posso modificare, non sono l'amministratore ma solo un client. Pertanto non posso aggiungere indici alle tabelle di origine. Lato server non posso agire. Il mio campo d'azione è lato client.
    La risoluzione delle date nella sintassi indicata non mi ha mai dato problemi. Nella tabella di origine la data è un campo Data/Ora, e accetta la sintassi #GG/MM/AAAA# senza problemi, la utilizzo da anni.
    Dire che funziona e dire che sia ottimizzato sono 2 cose diverse.
    Se il tuo server NON è JET, quella sintassi NON E' OTTIMIZZAZATA.
    Funziona ugualmente, ma se la tabella contiene 1Milione di Records... e tu volessi estrarre quelli di Oggi, il Server ti restituirebbe 1Milione si Records ed il Criterio di Data verrebbe applicato in locale, e questo se ti stai lamentando delle prestazioni non puoi pensare che vada bene...

    cla68 ha scritto:


    Se può essere utile per capire come potrei risolvere/migliorare posso aggiungere che creando una join inversa (right join anziché left join) sulla query finale il problema della velocità non esiste (la query è eseguita immediatamente in meno di 1sec), ma ovviamente non ottengo il risultato di records che mi serve.
    Definire meno di 1sec è un dato che a noi non dice nulla... se la Tabella ha una decina di migliaia di Records... meno di un secondo, può essere 0.8sec è una vita... impossibile...

    Mi fermo perchè non ho altre cose da avanzare... se hai modo/capacità di diagnosticare l'efficienza delle Query in modo tecnico con gli strumenti di analisi, si può fare qualche considerazione... se è una tua sensazione... si fa poco.
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    cla68 ha scritto:


    Ho controllato il Pool di connessioni e risultano tutte attive con permanenza nel pool impostata a 60 sec
    Mi sa che non stiamo dicendo la stessa cosa... non intendo la connessione delle Tabelle... ma la connessione di richiesta dati.
    Se apri un Recordset su una Tabella(quella lato server), viene instaurata una Connessione al momento in cui effettui un OpenRecordset, ma serve la validazione e la risposta di Ok..., quindi viene inviata la stringa SQL..., terminata l'elaborazione ServerSide della Query viene sparato al Client il pacchetto, e, se non serve o se non usata da altri, chiusa la connessione.
    Sicchè se rifai una nuova interrogazione, viene ripercorso il procedimento...!
    Il tempo di apri chiudi è molto rilevante, quindi si tende ad azzerarlo mantenendo un RS aperto... in quel modo rimane operativa una connessione.
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    La tabella collegata in odbc [TAB_PREZZI] ha 160982 records
    La query "010 elenco art forn" viene eseguita in un range (ho fatto diverse prove) tra 7 e 10 centesimi di secondo (cts) e ritorna 1538 records
    La query intermedia "020 01 list aperti" viene eseguita in un range 8-12 cts e ritorna 17378 rcds
    La query "020 02 list ultimi" viene eseguita in un range 10-15 cts e ritorna lo stesso numero di record 17378 (ma impiega molto a determinare il numero di record con un SQL COUNT sulla query, un paio di minuti)
    La query che vorrei invece migliorare viene eseguita in 12763 cts (poco più di 2 minuti) e ha ovviamente lo stesso numero di records della "010 elenco art forn" (1538 rcds) avendo un left join.
    Spero qs dati utili e meno generici rispetto a prima. Grazie l'eventuale aiuto/suggerimenti
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    cla68 ha scritto:


    La tabella collegata in odbc [TAB_PREZZI] ha 160982 records
    La query "010 elenco art forn" viene eseguita in un range (ho fatto diverse prove) tra 7 e 10 centesimi di secondo (cts) e ritorna 1538 records
    La query intermedia "020 01 list aperti" viene eseguita in un range 8-12 cts e ritorna 17378 rcds
    La query "020 02 list ultimi" viene eseguita in un range 10-15 cts e ritorna lo stesso numero di record 17378 (ma impiega molto a determinare il numero di record con un SQL COUNT sulla query, un paio di minuti)
    Già questa cosa non mi è affatto chiara... un Count dovrebbe essere più veloce della query, se eseguito lato server... fatico a capire.

    cla68 ha scritto:


    La query che vorrei invece migliorare viene eseguita in 12763 cts (poco più di 2 minuti) e ha ovviamente lo stesso numero di records della "010 elenco art forn" (1538 rcds) avendo un left join.
    Spero qs dati utili e meno generici rispetto a prima. Grazie l'eventuale aiuto/suggerimenti
    Se le query di base iniziali che riporti vengono eseguite con i tempi di cui sopra, quest'ultima non può impiegare 2minuti... almeno a livello intuitivo.
    Purtroppo non riesco a fare prove, ma l'unico dubbio che posso avere è che non venga eseguita dal Server... ma in locale.
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Per il conteggio record no ho fatto altro che qs istruzione, dove [020 02 list ultimi] è la query in access

    SELECT Count([020 02 list ultimi].OFQFOR) AS ConteggioDiOFQFOR
    FROM [020 02 list ultimi];

    Provo a convertire le query in pass-through per vedere se cambia qualcosa
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Dunque, ho convertito le due due query "010 elenco art forn" e "020 01 list aperti" in pass-through e si sono entrambe ulteriormente velocizzate.
    La query "020 02 list ultimi" non la posso però eseguire in pass-through perché è da collegare in join con la "020 01 list aperti"
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Ho eliminato tutte le query locali e ho scritto una query annidata pass-through. Funziona egregiamente ed è rapidissima. Devo però capire come parametrizzarla (le date dell'esempio le devo passare come parametri)

    SELECT b.OFQFOR, b.OFQART, c.MaxDiOFQDT1, c.OFQVAL, c.OFQQUO
    FROM
    (SELECT OFQFOR, OFQART
    FROM ASV1.OFQFI
    WHERE OFQDTI<='07/19/2017' and OFQDTI>='06/01/2017'
    GROUP BY OFQFOR, OFQART) b
    LEFT JOIN
    (SELECT a.OFQFOR, a.OFQART , a.MaxDiOFQDT1, ASV1.OFQFI.OFQVAL, ASV1.OFQFI.OFQQUO
    FROM (SELECT OFQFOR, OFQART, Max(OFQDT1) AS MaxDiOFQDT1 FROM ASV1.OFQFI WHERE OFQDT1<='07/19/2017' AND OFQSTS<>'4' GROUP BY OFQFOR, OFQART) a
    INNER JOIN ASV1.OFQFI ON (a.MaxDiOFQDT1 = ASV1.OFQFI.OFQDT1) AND (a.OFQART = ASV1.OFQFI.OFQART) AND (a.OFQFOR = ASV1.OFQFI.OFQFOR)) c
    ON (b.OFQART = c.OFQART) AND (b.OFQFOR = c.OFQFOR)
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Alla fine ho risolto passando in VBA.

    Sub prova()


    Dim sSQL As String
    Dim rs As ADODB.Recordset
    Dim cn As ADODB.Connection
    Dim data_ini, data_fin As Date
    Dim dIniStr, dFinStr As String

    data_ini = InputBox("inserire data inizio periodo")
    dIniStr = Format(data_ini, "mm/dd/yyyy")
    data_fin = InputBox("inserire data fine periodo")
    dFinStr = Format(data_fin, "mm/dd/yyyy")

    sSQL = "SELECT b.OFQFOR as CodFor, b.OFQART as CodArt, c.MaxDiOFQDT1 as ValidoDal, c.OFQVAL as Valuta, c.OFQQUO as PrezzoAct " _
    & "FROM " _
    & "(SELECT OFQFOR, OFQART " _
    & "FROM ASV1.OFQFI " _
    & "WHERE OFQDTI<='" & dFinStr & "' and OFQDTI>='" & dIniStr & "'" _
    & "GROUP BY OFQFOR, OFQART) b " _
    & "Left Join " _
    & "(SELECT a.OFQFOR, a.OFQART , a.MaxDiOFQDT1, ASV1.OFQFI.OFQVAL, ASV1.OFQFI.OFQQUO " _
    & "FROM (SELECT OFQFOR, OFQART, Max(OFQDT1) AS MaxDiOFQDT1 FROM ASV1.OFQFI WHERE OFQDT1<='07/19/2017' AND OFQSTS<>'4' GROUP BY OFQFOR, OFQART) a " _
    & "INNER JOIN ASV1.OFQFI ON (a.MaxDiOFQDT1 = ASV1.OFQFI.OFQDT1) AND (a.OFQART = ASV1.OFQFI.OFQART) AND (a.OFQFOR = ASV1.OFQFI.OFQFOR)) c " _
    & "ON (b.OFQART = c.OFQART) AND (b.OFQFOR = c.OFQFOR)"


    Set cn = New ADODB.Connection
    cn.Open "DSN=XXXXX;DRIVER=iSeries Access ODBC Driver; " & "SYSTEM = XXXX; UID = " & "XXXXXX" & ";PWD = " & "XXXXX"

    Set rs = New ADODB.Recordset
    rs.Open sSQL, cn, adOpenDynamic, adLockOptimistic


    rs.Close


    End Sub

    Grazie dell'aiuto.
  • Re: Velocizzare query incrociate basate sulla stessa tabella

    Bene che hai risolto (e mi sa Alex ci aveva visto giusto insistendo sul fatto che le query venivano risolte lato Client).
    Credo però che sia saggio effettuare un' ulteriore miglioria non prestazionale ma concettuale :
    ....."GROUP BY" .....
    .... "Left Join " ...
    .....

    Set rs = New ADODB.Recordset
    rs.Open sSQL, cn, adOpenDynamic, adLockOptimistic
    Se in una query esistono funzioni di aggregazione (Group By) la query diventa per definizione NON aggiornabile
    e quindi è inutile creare un recordset aggiornabile con relativo lock (crea uno snapshot / readonly).
    Ora la tua query è abbastanza complessa (left join + subquery con Group by) da diventare non aggiornabile : fai una verifica e se realmente NON è aggiornabile trasforma di conseguenza il tuo recordset.
Devi accedere o registrarti per scrivere nel forum
13 risposte