Creazione indice su tabelle ODBC

di il
8 risposte

Creazione indice su tabelle ODBC

Ciao!
Ho un database Microsoft Access con tabelle collegate ad un database PotsgreSQL tramite ODBC.
Sul database PostgreSQL ho già definito degli indici ma presumo che Access non li utilizzi data la lentezza di certe query (nel mio caso causata da un DCount in una query select).

Ho provato a creare manualmente un nuovo indice con l'istruzione:
CREATE INDEX idxIndice on public_nome_tabella (CodiceCliente)
ma mi segnala "Chiave primaria già esistente".
Il fatto è che non voglio creare una chiave primaria (infatti non ho inserito la dicitura 'WITH PRIMARY') ma solamente un indice sul campo 'CodiceCliente'.

Qualcuno si è imbattuto sul mio stesso problema e ha una soluzione?
Grazie 1000.

Davide.

8 Risposte

  • Re: Creazione indice su tabelle ODBC

    Stai facendo lavorare MALISSIMO il server...

    La query che hai scritto, anche se non la vedo..., se utilizza la funzione DCOUNT come pensi possa essere RISOLTA dal ServerEngine di PostGree...?
    Ovviamente la risposta è che non può essere risolta dal SERVER.

    Cosa accade in questo caso...? Che il SERVER non potendola soddisfare restituisce ad Access l'intero RECORDSET della tabella, costringendo JET all'elaborazione in LOCALE.

    Converrai che questo sistema non è utile e di norma quando si lavora con Server SQL da evitare.

    Se devi fare un COUNT lascialo fare al SERVER con delle SubSelect... in linguaggio comprensibile dal Server stesso.

    Purtroppo questo è un'errore molto comune che commettono gli utenti che passano a DB SQL pensando che siano la soluzione alla lentezza di JET.
  • Re: Creazione indice su tabelle ODBC

    Grazie Alex della risposta molto dettagliata e chiara.
    Però ho problemi ad usare anche le subquery con le maschere.
    Mi spego meglio, ho 2 semplici tabelle:
    Tabella 'Clienti'
    ------------------
    CodiceCliente
    Cliente
    
    
    Tabella 'Ordini'
    ------------------
    IDOrdine
    CodiceCliente
    Descrizione

    e una mia maschera di Access usa una delle due seguenti query:
    //Soluzione con 'DCount'
    SELECT CodiceCliente, Cliente, DCount("*"; "Ordini"; "CodiceCliente=" & [CodiceCliente]) AS NumOrdini FROM Clienti
    //Soluzione con sub query
    SELECT Clienti.CodiceCliente, Clienti.Cliente, Count(Ordini.CodiceCliente) AS NumOrdini FROM Clienti LEFT JOIN Ordini ON Clienti.CodiceCliente = Ordini.CodiceCliente GROUP BY Clienti.CodiceCliente, Clienti.Cliente
    Entrambe le query mi restituiscono gli stessi dati però la prima usandola sulla maschera mi permette di aggiungere/modificare/eliminare records anche se è lenta nella select;
    la seconda query è molto più veloce ma non posso più aggiungere/modificare/eliminare i record della maschera ma usarli solo in lettura.

    Non so perciò come posso avere una maschera che mi permetta di aggiungere/modificare/eliminare i clienti visualizzando per ognuno anche il numero di ordini e che non usi il 'DCount'.
    Mi dimentico qualcosa?

    Ho provato anche la funzione ECount scritta da Allen Browne da te menzionata ma la lentezza rimane.

    Spero di essere stato comprensibile,
    Grazie.
  • Re: Creazione indice su tabelle ODBC

    Credo che tu non abbia afferrato il concetto di base del problema...!

    La soluzione di Allen, concettualmente è da ignorare se usi un SERVER SQL perchè obbliga sempre il Server a restituire tutto il Dataset... la funzione ECOUNT come DCOUNT è interpretabile solo da Access... non dal server.

    Ho il sospetto che manchi qualche BASE formativa per parlare la stessa lingua...!

    Cerco di farti capire meglio... non confondere mai il CLIENT con il SERVER, purtroppo chi non ha esperienza ed inizia con Access subisce deformazioni non facilmente controllabili...!

    Se fai elaborare i dati al SERVER... questo è VELOCE e restituisce solo i dati corrispondenti... se fai interpretare ad Access il predicato fai fallire il lavoro del Server e lo sposti a JET che è lento.
    Ti faccio un'esempio per farti capire meglio
    Esempio 1
    Questa Query SELECT viene eseguita SERVER_SIDE e restituirà 1 solo Record corrispondente
    
    SELECT * FROM T1 WHERE Id=1
    In questo caso hai la MASSIMA EFFICIENZA.
    Esempio 2
    Questa Query viene eseguita in 2 tempi:
    
    SELECT * FROM T1 WHERE Id=Forms!NomeForm!ID
    Nonostante [Forms!NomeForm!ID]=1, quindi lo stesso valore del predicato precedente, il SERVER SQL non ha la più pallida idea di cosa sia [Forms!NomeForm!ID]... quindi non è in grado di interpretarla...!
    Cosa può fare il SERVER in questo caso, fregarsene del CRITERIO, ed eseguirà questa
    
    SELECT * FROM T1
    Poi JET applicherà al risultato della 1° query il CRITERIO perchè solo JET è in grado di risolvere l'espressione...

    Cosa hai ottenuto in questo caso....?
    Alla fine del risultato sarà la stessa cosa, ma mentre nel 1° caso ha lavorato correttamente il motore del DB(PostGree) ottimizzando sia i tempi di esecuzione sia il numero dei dati inviati in rete, nel 2° caso hai fatto lavorare inutilmente il Server(PostGree) obbligandolo ad inviare TUTTO IL MALLOPPONE, hai fatto lavorare inutilmente JET che è lento di suo...!

    Questo processo per è è trasparente e se non hai dimestichezza con i sistemi CLIENT-SERVER, con i TOOLS di sviluppo e di diagnostica propri degli RDBMS non sei in grado di sviluppare applicazioni CLIENT-SERVER degne di essere chiamate tali...!

    Quindi riassumendo una SubQuery che viene interpretata SERVERSIDE che faccia la stessa cosa che fa il DCOUNT si scrive in modo diverso... simile a questo:
    
    SELECT *, (Select COUNT(*) FROM ORDINI WHERE Ordini.CodiceCliente=Clienti.[CodiceCliente]) As NumOrdini FROM Clienti
    Come vedi la SUBQuery è semplice completamente interpretabile dal SERVER e, se hai strutturato in modo corretto INDICI e CHIAVI vedrai che ha una velocità di esecuzione elevata.

    Poi ci sono molte altre cose che dovresti approfondire, come l'uso delle StoredProcedure(che proprio in questo caso si adattano ad essere usate) soprattutto quando devi ottenere Query in sola LETTURA, le Query PASSTROUGHT che si usano per le STORED PROCEDURE/VISTE scritte direttamente serverside....

    Insomma passare da Access-JET ad Access-RDBMS serve fare passaggi che non sono il semplice uso del DB... ma serve studiare molti altri aspetti.
  • Re: Creazione indice su tabelle ODBC

    Ciao @Alex.
    Grazie della risposta. Sono rimasto sorpreso nel vederla così completa e professionale.

    Effettivamente non sono pratico del lato Server del database.
    Solitamente uso direttamente tabelle Access oppure le performance non erano la mia priorità.

    Scusa, non avevo letto completamente il codice della funzione di Allen, e avevo dato per scontato che essendo una rivisitazione del "DCount" la funzione fosse stata fatta in modo da usare solamente delle query sql interpretabili correttamente e direttamente dal Server.

    Anche "SELECT * FROM T1 WHERE Id=Forms!NomeForm!ID" ero sicuro che access avesse un parser e fare così la sostituzione di "Forms!NomeForm!ID" con il suo valore e quindi inviasse la query "SELECT * FROM T1 WHERE Id=1" al server.
    Tutte cose ho capito che mi sbagliavo.

    Per quanto riguarda la suddivisione tra client e server sono conscio che il più possibile dei calcoli è meglio li faccia il server, ma come appena accennato avevo una mia teoria (errata) di come Access passi le query al Server.


    Ho seguito i tuoi consigli facendo eseguire la query con subquery come da te descritto:
    SELECT *, (Select COUNT(*) FROM ORDINI WHERE Ordini.CodiceCliente=Clienti.[CodiceCliente]) As NumOrdini FROM Clienti
    Però i tempi rimangono lenti, più lenti del DCount ed inoltre il recordset così ottenuto risulta in sola lettura (non posso nè aggiungere/modificare/eliminare i dati così ottenuti).
    Ho verificato e l'indice "CodiceCliente" nella tabella "Ordini" è presente, infatti se eseguo la stessa query direttamente nel server postgre è istantanea.

    Ho provato anche ad eseguire una query PASSTROUGHT su Access e questa viene eseguita in modo istantaneo ma anche qui abbiamo un risultato in sola lettura.

    Non saprei cosa altro poter fare per risolvere il problema.

    Mi viene il dubbio che forse sto tentando di ottenere una cosa non ottenibile con Access: avere dei dati modificabili da Access usando interamente query dal lato server.
    Secondo te è possibile farlo o è meglio che mi rassegni e per il momento mi tengo questa lentezza?

    Grazie ancora,
    ciao.
  • Re: Creazione indice su tabelle ODBC

    Il problema non è Access in quanto CLIENT... ma più l'insieme dei concetti che a mio avviso ti hanno portato a sviluppare un'applicativo con i concetti di JET.

    Non confondere Access con JET.

    Quando lavori con solo ACCESS usi 2 pezzi...:
    1) ACCESS con form/report/VBA/macro
    2) JET con Tabelle/queries

    Quando lavori con un SERVER SQL, teoricamente devi abbandonare la 2°, cosa che risulta tecnicamente automatica quando relizzi i LINK alle Tabelle sul SERVER, ma che viene vanificata ogni qual volta introduci errori concettuali come quelli che ti ho evidenziato.

    Detto questo se la QueryPassTrought è VELOCE significa che l'elaborazione lato SERVER è corretta e che hai un'altro errore strutturale che purtroppo non saprei come individuare.
    Avendo ti fornito qualche concetto ora spero tu possa rianalizzare la cosa.

    Riassumento con Access(CLIENT) non hai alcuna limitazione prestazionale se non dovuta a tuoi errori.
  • Re: Creazione indice su tabelle ODBC

    Ho compreso quello che intendi tra la separazione tra Access ed il JET.

    Ho fatto delle verifiche eseguendo le seguenti query sulle tabelle 'Clienti' e 'Ordini':

    Query1 - con Group By:
    SELECT Clienti.CodiceCliente, Clienti.cliente, Count(Ordini.CodiceCliente) AS ConteggioDiCodiceCliente
    FROM Clienti LEFT JOIN Ordini ON Clienti.CodiceCliente = Ordini.CodiceCliente
    GROUP BY Clienti.CodiceCliente, Clienti.cliente
    ORDER BY Clienti.CodiceCliente;
    Risulta veloce ma giustamente di sola lettura (in quanto c'è il costrutto GROUP BY).


    Query2 - con DCount:
    SELECT Clienti.CodiceCliente, Clienti.cliente, DCount("CodiceCliente","Ordini","CodiceCliente=" & [CodiceCliente]) AS a
    FROM Clienti;
    Risulta lenta (per via del DCount) e giustamente con possibilità di scrittura.


    Query3 - con SubQuery:
    SELECT Clienti.CodiceCliente, Clienti.cliente, (select count (*) from Ordini where Ordini.CodiceCliente=Clienti.CodiceCliente) AS a
    FROM Clienti;
    Risulta molto molto lenta ( ) ed in sola lettura ( ).


    Query4 - di tipo Pass-Through:
    select CodiceCliente, cliente, (select count(*) from ord where Ordini.CodiceCliente = clienti.CodiceCliente) as numord from Clienti
    Risulta veloce ma di sola lettura (giustamente come tutte le query Pass-Through).



    Per assurdo la Query3, che non presenta costrutti particolari e dovrebbe essere elaborata interamente dal server, doveva essere veloce e doveva permettermi di poter avere accesso anche in scrittura ed invece nè l'uno e nè l'altro.

    Come dici tu ho rianalizzato la cosa, ho provato anche a fare un nuovo database PostgreSQL con solo la tabella Clienti e Ordini e con solo i campi che mi interessavano ma senza nessun risultato.
    Non so, forse c'entrano i driver/impostazioni ODBC, la versione di Microsoft Access o di chissà cos'altro.

    Ho attivato il log del server Postgre per verificare come vengono elaborate le query da parte del server e ho constatato che le query veloci (GROUP BY e PASS-THROUGH) sono state lasciate quasi inalterate dal server, mentre quelle lente (DCount e SubQuery) sono state elaborate dal server in ulteriori molte query e cursori. Capisco il DCount ma la Query con la SubQuery doveva lasciarla inalterata. Mi viene il dubbio che c'entri il driver ODBC.

    Attualmente sono riuscito a velocizzare di molto disattivando la proprietà "Use Declare/Fetch" del driver ODBC "PostgreSQL ANSI".

    Per il momento continuerò ad usare il DCount fino a che mi imbatterò più avanti in qualche soluzione.

    Comunque grazie dei consigli ricevuti.
    Ciao.
  • Re: Creazione indice su tabelle ODBC

    Le considerazioni che fai sono tutte corrette, c'è una sola cosa che non saprei spiegarmi, vale a dire che la QuryPT sia più veloce della Query Standard con SubSelect...(Query3)

    Di fatto la Query3 è normale sia ReadOnly avendo una SubSelect, ma che sia lenta eseguita come Query da Access mi risulta molto strano, l'unico motivo potrebbe essere dovuto all'eventuale campo CodiceCliente non correttamente configurato nelle tabelle come FK o come INDEX...
    Non ho mai lavorato con PostGree, peranto non conosco gli strumenti di Diagnostica che mette a disposizione... SQLSERVER ad esempio consente con il QueryAnalizer di capire come le query vengono interpretate dal SERVER prima di essere eseguite, ma mi pare sia il lavoro che hai ben fatto anche tu...

    Una tecnica che si usa spesso con i DBMS_SERVER è quella di usare una fonte dati ReadOnly per la visualizzazione e puntare alla fonte in scrittura solo se l'esigenza richiede la modifica, aprendo una Maschera SINGOLA per l'editing.
    Questa tecnica si usa spesso in ambiente MULTUTENTE...
    Otterresti una ottimizzazione dei risultati in visualizzazione, ed anche per la modifica, usando la PK per l'estrazione del Record Singolo avresti un processo ottimizzato, lasciando LIBERO il precedente Recordset ad eventuali altri UTENTI in lettura...

    Buon lavoro.
  • Re: Creazione indice su tabelle ODBC

    Ok,
    Terrò in seria considerazione l'uso di maschere in sola lettura per le interrogazioni ed il riepilogo dei dati e la possibilità di aprire maschere singole per le modifiche dei record.
    In questo modo guadagno anche di ridurre le modifiche accidentali di certi utenti.

    Grazie 1000,
    Ciao.
Devi accedere o registrarti per scrivere nel forum
8 risposte