I Cursori SPT (SQL Pass-Through) - Compendio pratico su come usarli

 

© Articolo di: Emanuele Bonin © Settembre 2002 - Riproduzione vietata

Cosa sono ?

I cursori SPT sono il risultato, in locale (su client), di una richiesta (SQL-QUERY) di dati fatta tramite tecnologia Pass-Through ad un server remoto.
Quindi abbiamo a che fare con tavole temporanee (come per i normali cursori Visual Fox Pro) le quali comunque non perdono memoria della loro provenienza. Prova ne è il fatto che non è possibile eseguire un ALTER TABLE su di un cursore SPT, in quanto tali operazioni non sono permesse proprio da Visual FoxPro tramite l'emissione di un errore:

Errore 1525 - Function is not supported on remote tables.

Questo cordone ombelicale tenuto da VFP tra il cursore SPT e il SERVER da cui ha origine, esiste in quanto in ogni momento è possibile fare in modo che eventuali cambiamenti effettuati nel cursore locale possano essere riversati sul server remoto.

Come si creano i cursori SPT ?

Innanzitutto per creare i cursori SPT bisogna aprire un canale di dialogo con il SERVER remoto dal quale vogliamo estrapolare i dati. Una volta aperto il canale di comunicazione tramite opportuni comandi si potranno eseguire le richieste al server.

Come si apre un canale di comunicazione con il server

Per aprire il canale di comunicazione con il server contenente i dati, VFP utilizza i driver ODBC, quindi dobbiamo essere certi di avere a disposizione i driver ODBC per il dialogo con il tipo di SERVER di cui abbiamo bisogno.
Il passo successivo è quello di creare la cosidetta Stringa di connessione, cioè la sequenza di parole chiave che indica al driver ODBC dove si trova il server e quali parametri (password, username ecc..) utilizzare per collegarsi ad esso, tale stringa di connessione varia a seconda del driver ODBC che si vuole utilizzare, tranne che per alcune parole chiave.
A questo punto o si conoscono a priori i nomi dei parametri e quali parametri inserire nella stringa di connessione o si fa in modo che sia il driver ODBC stesso a chiederci tramite una maschera di input abbastanza standard i parametri da utilizzare, in questo modo la stringa di connessione verrà costruita all'interno del driver stesso, e noi ci troveremo automagicamente il canale di comunicazione aperto per il dialogo con il server.

Per fare in modo che sia il driver stesso a costruirsi la stringa di connessione dobbiamo perlomeno conoscere il nome col quale il driver ODBC si fà riconoscere, tale nome è un parametro che è sempre presente all'interno della stringa di connessione e tramite questi riusciamo a ricavari tutti gli altri parametri. Per conoscere l nome con cui il driver ODBC si distingue, di norma basta cercare nel manuale di help del server su cui si vuole lavorare.
Il driver per Microsoft SQL Server si identifica semplicemente con "SQL Server", mentre per collegarsi ad una sorgente dati di Visual FoxPro avremo "Microsoft Visual FoxPro Driver".

L'apertura del canale di comunicazione a questo punto è uno scherzo, sarà sufficiente lanciare il seguente comando:

nHandle = SQLSTRINGCONNECT("DRIVER=SQL Server")

Se si vuole aprire il canale di comunicazione con un Server di tipo Microsost SQL Server

nHandle = SQLSTRINGCONNECT("DRIVER=Microsoft Visual FoxPro Driver")

Se si vuole aprire il canale di comunicazione con una sorgente dati Visual FoxPro.

La variabile nHandle conterrà il valore che servirà per indicare a Visual FoxPro quale canale utilizzare per le future richieste da fare al Server.
Se nHandle non è > 0 dopo l'esecuzione del comando, vuol dire che la connessione non è andata a buon fine.

Nel caso in cui tutto sia andato bene possiamo conoscere la sintassi completa della stringa di connessione che VFP ha utilizzato per collegarsi al Server semplicemente scrivendo questo comando:

cString = SQLGETPROP(nHandle, 'CONNECTSTRING')

cString conterrà qualcosa di simile:

DRIVER=SQL Server;SERVER=SRV;UID=sa;PWD=Pippo;APP=MyApp;WSID=;DATABASE=master;LANGUAGE=Italiano

Alla prossima connessione che verrà eseguita si potrà sfruttare tale stringa di connessione sostituendo il valore dei parametri (evidenziati in rosso) con i valori opportuni per la connessione ad un altro Server (oppure allo stesso usando la stringa coì com'è) senza che venga richiesta l'immissione dei parametri, semplicemente scrivendo:

SQLSTRINGCONNECT(cString).

Una volta che si vuole chiudere la connessione con il server sarà sufficiente scrievre:

SQLDISCONNECT(nHandle).

Creazione di un semplice cursore SPT

Per creare un cursore SPT, avendo in nHandle il valore chiave per accedere al canale di comunicazione, basterà usare la funzione SQLEXEC, la quale si incarica di passare al server le richieste.
Qualora tali richieste implichino un ritorno di dati da parte del server, questi dati, di qualsiasi natura siano (record di tavole remote o semplici valori tornati dal server), verranno immagazzinati in un cursore SPT locale da VPF.
Supponendo che esista una tavola CLIENTI su server, sarà possibile richiedere la lista dei nomi dei clienti maschi scrivendo qualcosa del genere:

SQLEXEC(nHandle, "SELECT COGNOME, NOME FROM CLIENTI WHERE SESSO = 'M'")

Alla fine dell'esecuzione di tale comando avremo a disposizione un cursore SPT il cui ALIAS sarà "SqlResult".

Nel caso volessimo imporre il nome dell'alias per il cursore da creare, sarà sufficiente inserirlo come secondo parametro alla precedente funzione:

SQLEXEC(nHandle, "SELECT COGNOME, NOME FROM CLIENTI WHERE SESSO = 'M'", "CUR")

Con tale comando avremo restituito un cursore il cui alias è "CUR".

Peculiarità di un cursore SPT

Un cursore SPT ha alcune peculiarità che bisona avere ben presenti.

- Innanzitutto, un cursore SPT, al contrario di un cursore normale creato da una SELECT ... INTO ... (senza l'opzione READWRITE dal VFP 7.0 in poi), è scrivibile (ma non ALTERABILE, come già visto);

- Un cursore SPT nasce sempre bufferizzato e non può essergli tolto il buffer, si può solo modificare usando o buffer di riga o buffer di tavola

Come aggiornare un cursore SPT su Server

Una volta reperiti i dati che servono in un cursore SPT, si possono eseguire delle modifiche a tale cursore (aggiungere togliere records, modificare i valori dei campi ecc...) e quindi riportare tali modifiche sul server, principalmente in due maniere:

L'intento di questo compendio, è quello di spiegare come eseguire gli aggiornamenti con questa seconda tecnica.


Come rendere "sensibile" un cursore

Ogni cursore SPT possiede una serie di propietà, le quali ne determinano il suo comportamento locale ma anche verso la fonte dati remota.

Tali properties possono essere reperite e modificate (non tutte, alcuno sono read-only), grazie all'uso delle funzioni:

Al fine di automatizzare gli update su server ("sensibilizzare il cursore"), sarà indispensabile esaminare le seguenti properties:

TABLES Property
Tramite questa property indichiamo al cursore SPT quali tavole (se più d'una dovranno essere separate da virgole), coinvolte nel reperimento dei dati, potranno essere aggiornate.

KEYFIELDLIST Property
Questa property indica quali solo i campi di chiave primaria per l'identificazione univoca dei records (se vi sarà più d'una chiave primaria dovranno essere separate da virgole), per ognuna delle tavole indicate in TABLES. Nel caso in cui venga a mancare una chiave primaria, la tavola corrispondente non potrà essere aggiornata.

UPDATENAMELIST Property
Con questa property indica al cursore la corrispondenza tra campi locali e campi remoti che verranno coinvolti dall'aggiornamento delle tavole. Di norma i nomi remoti corrispondono ai nomi locali dei campi, tranne nei casi in cui si sia utilizzata la clausola AS all'interno delle select (vedi successivi esempi).

La sintassi della property è la seguente:

<nome campo locale1> <nome completo campo remoto1>, <nome campo locale2> <nome completo campo remoto2 > ....

nella lista deve comparire il campo chiave primaria, pena il fallimento degli eventuali update.

UPDATABLEFIELDLIST Property
In questa property si indicano tutti I campi del cursore locale (divisi da virgole) che possono essere aggiornati.

WHERETYPE Property
Tramite questa property viene indicato come deve essere utilizzata la chiave primaria, cioè quali campi oltre alla chiave primaria si utilizzeranno per riconoscere i record da aggiornare.
I valori possibili sono:

1 - Solo chiave primaria
2 - Chiave Primaria e tutti i campi aggiornabili
3 - Chiave Primaria e tutti i campi che sono stati modificati
4 - Chiave Primaria e un campo detto di TimeStap

SENDUPDATES Property
Questa property è la più semplice, ma senza di essa non sarebbe possibile rendere aggiornabile automaticamente il cursore SPT, infatti per rendere aggiornabile il cursore bisogna porre a .T. tale property.

Ed ora una serie di esempi pratici

Supponiamo di avere un database remoto in cui appaiono due Tavole:

CLIENTE
Id_Cliente        Campo ID univoco (Chiave Primaria)
Nome               Nome del Cliente
Id_Banca         Compo ID della Banca usata dal cliente

BANCA
Id_Banca         Campo ID univoco (Chiave Primaria)
Nome               Nome Della Banca

La Tavola Banca risulta essere vincolata alla tavola Cliente dal campo id_Banca.
La variabile nHandle già contiene il valore dell'Handle aperto per il dialogo con il server remoto.

Esempio 1: Aggiornamentio semplice di un cursore collegato ad una Tavola Remota

* Seleziono tutti i clienti il cui nome inizia per M
SQLEXEC(nHandle, "SELECT * FROM CLIENTE WHERE Nome LIKE 'M%'", "CLIENTE")

* Impongo il Buffer a 5 per il cursore CLIENTE
CURSORSETPROP("Buffering" , 5,"CLIENTE")

CURSORSETPROP("UpdateNameList" , " ID_CLIENTE CLIENTE.ID_CLIENTE, NOME CLIENTE.NOME, ID_BANCA CLIENTE.ID_BANCA","CLIENTE")
CURSORSETPROP("UpdatableFieldList", "ID_CLIENTE, NOME, ID_BANCA", "CLIENTE")
CURSORSETPROP("KeyFieldList" , "ID_CLIENTE", "CLIENTE")
CURSORSETPROP("Tables" , "CLIENTE", "CLIENTE")
CURSORSETPROP("WhereType" , 1, "CLIENTE")
CURSORSETPROP("SendUpdates" , .T., "CLIENTE")

Eventuali cancellazioni/inserimenti di records oppure modifiche nei campi del cursore CLIENTE saranno riportate su server automaticamernte in seguito ad una opportuna istruzione di TABLEUPDATE(...).

IF !TABLEUPDATE(2, .T., 'CLIENTE')
MESSAGGEBOX(MESSAGE())
ENDIF
USE IN CLIENTE
SQLDISCONNECT(nHandle)

Esempio 2: Aggiornamento di un cursore creato usando dati da due tavole remote

* Seleziono dati da CLIENTE e BANCA dove l'Id_Banca = 4
TEXT TO cSql NOSHOW
SELECT
C.Id_CLIENTE,
C.Nome,
B.Id_Banca,
B.Nome AS Banca_Nome
FROM CLIENTE C
LEFT OUTER JOIN BANCA B ON C.Id_Banca = B.Id_Banca
WHERE B.id_Banca = 4
ENDTEXT


SQLEXEC(nHandle, cSql, "CLIENTE_BANCA")

CURSORSETPROP("Buffering" , 5,"CLIENTE_BANCA")

CURSORSETPROP("UpdateNameList" , "ID_CLIENTE CLIENTE.ID_CLIENTE, NOME CLIENTE.NOME, ID_BANCA BANCA.ID_BANCA, BANCA_NOME BANCA.NOME","CLIENTE_BANCA")

* In UPDATABLEFIELDLIST Indico a VFP che il campo BANCA_NOME è un campo
*
che esiste solo nel cursore locale, e che deve essere comunque aggiornabile,
* ciò è possibile grazie all'associazione fatta nella property UPDATENAMELIST
*
(vedi parte sottolineata) dove si indica il corrispondente nome remoto

CURSORSETPROP("UpdatableFieldList" , "ID_CLIENTE, ID_BANCA, NONME, BANCA_NOME","CLIENTE_BANCA")


CURSORSETPROP("KeyFieldList" , "ID_BANCA, ID_CLIENTE", "CLIENTE_BANCA")

CURSORSETPROP("Tables" , "CLIENTE, BANCA", "CLIENTE_BANCA")
CURSORSETPROP("WhereType" , 1, "CLIENTE_BANCA")
CURSORSETPROP("SendUpdates" , .T., "CLIENTE_BANCA")

IF !TABLEUPDATE(2, .T., 'CLIENTE_BANCA')
MESSAGGEBOX(MESSAGE())
ENDIF
USE IN CLIENTE_BANCA
SQLDISCONNECT(nHandle)

Eventuali TABLEUPDATE fatti dopo aver modificato il cursore CLIENTE_BANCA si riverseranno su server, con la differenza, rispetto all'esempio 1 che le modifiche o aggiunte o cancellazioni effettuate andranno a occare entrambe le tavole CLIENTE e BANCA (naturalmente se le modifiche del cursore saranno tali da toccarle entrambe).

Se si volesse evitare l'aggiornamento di una delle tue tavole, sarebbe sufficiente reimpostare la property tables senza tale tavola al suo interno, anche se la cosa più pulita è quella di eliminare tutti i riferimenti ai campi della tavola da non aggiornare nelle altre properties.

Vantaggi e Svantaggi dei Cursori SPT

Anche se risulta difficile definire quali siano i reali vantaggi nell'utilizzo dei cursori SPT, in quanto alcuni di essi risulta soggettivo a chi programma, verranno elencate alcune caratteristiche ritenute vantaggiose nell'utilizzo dei cursori SPT:

Nei cursori SPT non vi è la possibilità di fare una REQUERY dei dati, la cosa deve essere risolta rieseguendo i passi di SQLEXEC(...) (rieseguendo una SQLEXEC() per creare un cursore SPT già esistente non serve prima chiudere tale cursore) ed eventualmente di "sensibilizzazione" qualora si vogliano aggiornare i dati, creando una funzione apposita il tutto si risolve.


Conclusioni e consigli

L'uso di cursori SPT non è l'unica maniera per lavorare in tecnologia CLIENT/SERVER, ma una volta appresa la tecnica, che può sembrare tediosa all'inizio, risulterà semplicissimo lavorare in ambienti client/server.

La parte più tediosa risulta sicuramente essere la parte riguardante la "sensibilizzazione" del cursore SPT, ma, come presumo che facciano tutti coloro che usano cursori SPT, si può benissimo costruire una funzione a puntino che esegua per noi tutte le tediose CURSORSETPROP(...) nella maniera giusta, il che risulterà più facile se il database utilizza delle regole fisse per i nomi dei campi, che magari riconducano alla tavola di appartenenza, ma questo è solo un suggerimento.

 

© Articolo di: Emanuele Bonin © Agosto 2002 - Riproduzione vietata

© FoxPro e Visual FoxPro sono un marchi registrati da Microsoft Corporation

 



Data: 07/09/2002
webmaster@foxitaly.com

 

dal 22 Giugno 1999