Simulazione di istruzioni di eliminazione e aggiornamento posizionate

Se l'origine dati non supporta istruzioni di eliminazione e aggiornamento posizionate, il driver può simulare queste istruzioni. Ad esempio, la libreria di cursori ODBC simula le istruzioni di eliminazione e aggiornamento posizionate. La strategia generale per simulare istruzioni di eliminazione e aggiornamento posizionate consiste nel convertire le istruzioni posizionate in quelle con ricerca. Questa operazione viene eseguita sostituendo la clausola WHERE CURRENT OF con una clausola WHERE con ricerca che identifica la riga corrente.

Ad esempio, poiché la colonna CustID identifica in modo univoco ogni riga nella tabella Customers, l'istruzione di eliminazione posizionata

DELETE FROM Customers WHERE CURRENT OF CustCursor  

potrebbe essere convertita in

DELETE FROM Customers WHERE (CustID = ?)  

Il driver può usare uno degli identificatori di riga seguenti nella clausola WHERE:

  • Colonne i cui valori servono per identificare in modo univoco ogni riga della tabella. Ad esempio, la chiamata a SQLSpecialColumns con SQL_BEST_ROWID restituisce la colonna o il set di colonne ottimali che servono a questo scopo.

  • Pseudo-colonne, fornite da alcune origini dati, allo scopo di identificare in modo univoco ogni riga. Possono anche essere recuperabili chiamando SQLSpecialColumns.

  • Un indice univoco, se disponibile.

  • Tutte le colonne nel set di risultati.

Le colonne esatte che un driver deve usare nella clausola WHERE che costruisce dipendono dal driver. In alcune origini dati, determinare un identificatore di riga può essere costoso. Tuttavia, è una procedura più veloce da eseguire e garantisce che un'istruzione simulata aggiorni o elimini al massimo una riga. A seconda delle funzionalità del sistema DBMS sottostante, l'uso di un identificatore di riga può essere costoso da configurare. Tuttavia, è una procedura più veloce da eseguire e garantisce che un'istruzione simulata aggiorni o elimini una sola riga. L'opzione di usare tutte le colonne nel set di risultati è in genere molto più semplice da configurare. Tuttavia, è più lenta eseguire e, se le colonne non identificano in modo univoco una riga, possono comportare l'aggiornamento o l'eliminazione involontaria di righe, soprattutto quando l'elenco di selezione per il set di risultati non contiene tutte le colonne presenti nella tabella sottostante.

A seconda di quale delle strategie precedenti è supportata dal driver, un'applicazione può scegliere quale strategia deve usare il driver con l'attributo di istruzione SQL_ATTR_SIMULATE_CURSOR. Anche se potrebbe sembrare strano che un'applicazione rischi involontariamente l'aggiornamento o l'eliminazione di una riga, l'applicazione può rimuovere questo rischio facendo in modo che le colonne nel set di risultati identifichino in modo univoco ogni riga nel set di risultati. In questo modo, il driver non dovrà eseguire questa operazione.

Se il driver sceglie di usare un identificatore di riga, intercetta l'istruzione SELECT FOR UPDATE che crea il set di risultati. Se le colonne nell'elenco di selezione non identificano in modo efficace una riga, il driver aggiunge le colonne necessarie alla fine dell'elenco di selezione. Alcune origini dati hanno una sola colonna che identifica sempre in modo univoco una riga, ad esempio la colonna ROWID in Oracle; se tale colonna è disponibile, il driver la usa. In caso contrario, il driver chiama SQLSpecialColumns per ogni tabella nella clausola FROM per recuperare un elenco delle colonne che identificano in modo univoco ogni riga. Una restrizione comune risultante da questa tecnica è che la simulazione del cursore ha esito negativo se nella clausola FROM sono presenti più tabelle.

Indipendentemente dal modo in cui il driver identifica le righe, in genere rimuove la clausola FOR UPDATE OF dall'istruzione SELECT FOR UPDATE prima di inviarla all'origine dati. La clausola FOR UPDATE OF viene usata solo con istruzioni di aggiornamento ed eliminazione posizionate. Le origini dati che non supportano le istruzioni di aggiornamento ed eliminazione posizionate in genere non supportano ciò.

Quando l'applicazione invia un'istruzione di aggiornamento o eliminazione posizionata per l'esecuzione, il driver sostituisce la clausola WHERE CURRENT OF con una clausola WHERE contenente l'identificatore di riga. I valori di queste colonne vengono recuperati da una cache gestita dal driver per ogni colonna usata nella clausola WHERE. Dopo che il driver ha sostituito la clausola WHERE, invia l'istruzione all'origine dati per l'esecuzione.

Si supponga, ad esempio, che l'applicazione invii l'istruzione seguente per creare un set di risultati:

SELECT Name, Address, Phone FROM Customers FOR UPDATE OF Phone, Address  

Se l'applicazione ha impostato SQL_ATTR_SIMULATE_CURSOR per richiedere una garanzia di univocità e se l'origine dati non fornisce una pseudo-colonna che identifica sempre in modo univoco una riga, il driver chiama SQLSpecialColumns per la tabella Customers, individua che CustID è la chiave della tabella Customers e lo aggiunge all'elenco di selezione, quindi rimuove la clausola FOR UPDATE OF:

SELECT Name, Address, Phone, CustID FROM Customers  

Se l'applicazione non ha richiesto una garanzia di univocità, il driver rimuove solo la clausola FOR UPDATE OF:

SELECT Name, Address, Phone FROM Customers  

Si supponga che l'applicazione scorra il set di risultati e invii la seguente istruzione di aggiornamento posizionata per l'esecuzione, dove Cust è il nome del cursore sul set di risultati:

UPDATE Customers SET Address = ?, Phone = ? WHERE CURRENT OF Cust  

Se l'applicazione non ha richiesto una garanzia di univocità, il driver sostituisce la clausola WHERE e associa il parametro CustID alla variabile nella cache:

UPDATE Customers SET Address = ?, Phone = ? WHERE (CustID = ?)  

Se l'applicazione non ha richiesto una garanzia di univocità, il driver sostituisce la clausola WHERE e associa i parametri Nome, Indirizzo e Telefono nella clausola alle variabili nella cache:

UPDATE Customers SET Address = ?, Phone = ?  
   WHERE (Name = ?) AND (Address = ?) AND (Phone = ?)