SQL Server via ODBC

Ciao a tutti, nella mia applicazione devo accedere ad un Database SQL Server tramite ODBCDatabase. Avrei la necessit di bloccare un record quando lo leggo per aggiornarlo successivamente, onde evitare che, nel frattempo, un altro utente acceda allo stesso record per aggiornarlo.
Ho cercato nel forum in lungo e in largo, ed anche in Internet, ma non ho trovato praticamente nulla che mi dica se si pu fare e come.

Qualcuno mi pu aiutare?

Grazie.

Nedi

Non c’ proprio nessuno che mi possa dare qualche consiglio?

I moved my post here https://forum.xojo.com/45562-sql-server-and-odbc :slight_smile:

Puoi avere due approcci:

  1. Utilizzare una tabella, come suggerito anche da Julian, e impostare le informazioni di blocco li (ad esempio tabella, id record, chi , quando)
    In questo modo devi solo controllare se il blocco esiste, non rendere possibile l’esecuzione dell’editing e verificare che il blocco non sia “antico” ovvero figlio di un crash.

  2. Utilizzare info di ultima modifica e autore a livello di record (consigliabile in molte situazioni se non tutte) e al momento di salvare il record, se questo stato modificato nel frattempo adottare le giuste strategie (accettare sempre le nuove info, confrontarle, rifiutarle)

Sebbene diversi, entrambi soffrono dello stesso problema: quando un utente modifica un record, gli altri che lo stanno osservando non sono in grado di verificare se il record che stanno vedendo nel frattempo modificato o meno (a meno di usare metodi di broadcasting appositi)

In ogni caso io preferisco il secondo metodo (permette anche di gestire sistemi esterni all’applicazione una volta definite le strategie/ i permessi di aggiornamento) e permette di rimandare alla fine dell’osservazione il processo (se lo blocco e vado a prendere un caff posso mandare in deadlock il sistema…)

Le operazioni devono essere il pi possibile atomiche e quindi il blocco deve avvenire, preferibilmente, all’interno di una transazione ovvero nel momento in cui salvi. Se hai necessit di bloccare un record per aggiornarne diversi, devi farlo in una transazione; se devi controllare che tutto avvenga potenzialmente in modo corretto devi fare una verifica (finale) che controlli le precondizioni per poter salvare.

Grazie Antonio per la tua risposta.
Preferirei una soluzione più diretta e demandata al motore del database (o al provider), cosa che facevo con Visual Basic e che per oltre 30 anni facevo con DB2 IBM, dove il lock del record che vuoi aggiornare è gestito non dal mio software (che si limita a segnalare la cosa all’utente).
Questo è un esempio di come funzionava il tutto con Visual Basic e SQL Server:

Public Sub GetData(Anno As Integer, NumPrev As Long, LockRecord As Boolean)

  ' Trova il record richiesto e imposta le proprietà.
  ' Vengono passati la chiave del record da rintracciare
  ' ed il flag che dice se bloccare il record oppure no.

  Dim SQL     As String

  ' Apertura connessione al Database
  If ConDB.State = adStateClosed Then
    ConDB.Open sConnectionString, sUser, sPassword
  End If

  SQL = "SELECT * FROM Preventivi_T "
  SQL = SQL & " WHERE PtAnno = " & Anno
  SQL = SQL & " AND PtNumPrev = " & NumPrev

' Gestione lock record
  If LockRecord Then
    ConDB.Execute ("SET LOCK_TIMEOUT 0")
    RS_Preventivi_T.CursorLocation = adUseServer
    RS_Preventivi_T.LockType = adLockPessimistic
  Else
    RS_Preventivi_T.CursorLocation = adUseClient
    RS_Preventivi_T.LockType = adLockReadOnly
  End If

  RS_Preventivi_T.ActiveConnection = ConDB
  RS_Preventivi_T.CacheSize = 1
  RS_Preventivi_T.CursorType = adOpenKeyset
  RS_Preventivi_T.Source = SQL

  RS_Preventivi_T.Open , , , , adCmdText

  On Error GoTo Errore
  RS_Preventivi_T.MoveFirst ' qui SQL Server blocca il record
  On Error GoTo 0

....
....

Come si può vedere, tutto quello che io devo fare è decidere se, in fase di lettura di un record, bloccarlo oppure no (parametro LockRecord): se si allora devo impostare un paio di proprietà del Recordset, ed il gioco era fatto.

Vorrei capire se in Xojo posso fare una cosa simile.

Grazie mille.

This is record locking which is similar to transaction locking. This will only lock the database in the few milliseconds it takes to update the record so you don’t have someone else come in and change it while you are editing.

What you are after is a 5+ minute lock on the data while someone is inside the edit screen of their application, am I correct in that assumption or did I read you wrong?

Hi Julian, the steps are:
a) GetData, that is read the record I want to update and lock it
b) put data on the screen and let user to edit them
c1) update record and therefore unlock the record
c2) quit the editing and explicitly unlock the record

It’s true that if the user left the data (and the record locked) for 5 minutes or more, nobody else will be able to modify that record (although everybody can read it).

However the same will occur if I adopt your solution: if the user lock a record (write a record in the table that maintains the lock) and then stands for 15 minutes without updating nor abandoning the record, nobody else will be able to update it.

So I prefer a solution like the one I posted here.

You can handle record locking level in your connexion string, through the cursor parameters. Below is an extract of this Microsoft page, which explains how to do it. Based on your description, you seem to want “pessimistic concurrency control”. The select statement will determine whether a single record or numerous records will be locked.

[quote]Types of Concurrency
When many people attempt to modify data in a database at the same time, a system of controls must be implemented so that modifications made by one person do not adversely affect those of another person. This is called concurrency control.

Concurrency control theory has two classifications for the methods of instituting concurrency control:

Pessimistic concurrency control

A system of locks prevents users from modifying data in a way that affects other users. After a user performs an action that causes a lock to be applied, other users cannot perform actions that would conflict with the lock until the owner releases it. This is called pessimistic control because it is mainly used in environments where there is high contention for data, where the cost of protecting data with locks is less than the cost of rolling back transactions if concurrency conflicts occur.

Optimistic concurrency control

In optimistic concurrency control, users do not lock data when they read it. When a user updates data, the system checks to see if another user changed the data after it was read. If another user updated the data, an error is raised. Typically, the user receiving the error rolls back the transaction and starts over. This is called optimistic because it is mainly used in environments where there is low contention for data, and where the cost of occasionally rolling back a transaction is lower than the cost of locking data when read.

SQL Server supports a range of concurrency control. Users specify the type of concurrency control by selecting transaction isolation levels for connections or concurrency options on cursors. These attributes can be defined using Transact-SQL statements, or through the properties and attributes of database application programming interfaces (APIs) such as ADO, ADO.NET, OLE DB, and ODBC.[/quote]

Thank Louis, the problem is not the theory (I know it quite well), the problem is how to do that with Xojo…

My apologies if I stated the obvious. Hopefully, it will be useful to others reading the thread.

I would suggest that you don’t use the plugin, but manage the connexion manually. Your connexion string includes parameters for the type of cursor and lock type. I use ADO myself in that manner. The ODBC strings are somewhat similar. If your application runs on Windows, the strings are documented in many places. For other platforms, I am not certain how to handle it.

Now, perhaps the plugin includes some form of control for the cursor type and lock granularity, but I did not find any when I played with it a few years ago.

I did a bit more reading on the topic. It would appear that the majority of ODBC drivers support only optimistic locking. From what I read on the MS documentation, I understand that the current Microsoft ODBC drivers for SQL Server only support optimistic locking.

Unless you expect a large number of concurrent updates to the same records, optimistic locking is probably the most efficient option.

Hi Louis, you suggest me not to use the plugin: how can I define the Database? Now I have

Dim DB As ODBCDatabase
DB = New ODBCDatabase

If I would use ADO (when I programmed with Visual Basic ADO was the standard in my applications) what kind of Database should I declare?
As I am new of Xojo I am in trouble!!

If your application is going to be cross-platform, then the plugin is likely only way to go. For Windows-only, you can also use ADO.

Here is an ADO example provided by Jrme Leray in another thread (I found his before mine…)

[code] Dim adodbConnection as new OLEObject( “ADODB.Connection” )
Dim adodbCommand as new OLEObject( “ADODB.Command” )
Dim adodbRecordSet as OLEObject
Dim adodbFields as OLEObject
Dim connectionString, SQL as String
Dim fieldCount as integer
connectionString = “Provider=SQLOLEDB; Data Source=SQLSRV; Initial Catalog=MY_DB; User Id=MY_USER; Password=MY_PWD”

adodbConnection.Open(connectionString)
adodbCommand.ActiveConnection = adodbConnection

SQL="Select ID, RESULTS  from MY_TABLE WHERE KPI like '%2015' "

adodbCommand.ActiveConnection = adodbConnection
adodbCommand.CommandText = SQL
adodbRecordSet = adodbCommand.Execute
adodbFields = adodbRecordSet.Fields
fieldCount = adodbFields.Count
while not adodbRecordSet.EOF
adodbFields = adodbRecordSet.Fields
if adodbRecordset.Fields(“ID”).value <> nil then
Labelglobal1.Text=adodbRecordset.Fields(“RESULTS”).value
end if
adodbRecordSet.MoveNext
wend[/code]

This Microsoft page describes how to connect to a database with ODBC. It is similar to the above ADO example.

You will have to include a module with all the ADODB constants in your project or use their numeric value.

Ok, thanks Louis! That’s what I was looking for.
Now I have to decide between ADO (and loose the cross-platform) and ODBC.

Thank you very much!

Ciao, io ho una classe che ho chiamato Tablelock;
questa in pratica una classe che gestisce una tabella sul DB con tutti le tabelle lockate;
in pratica quando devo aggiornare record da pi tabelle la avvio impostando il nome delle tabelle che devo riservare;
quindi verifico anche che le tabelle che devo utilizzare non siano lockate da un’altro utente con altre procedure
se questo si verifica il tablelock avvia un ciclo di attesa ( Mostrando un messaggio che indica quale utente st bloccando le tabelle ) dove di tanto in tanto verifica che le tabelle lockate si siano sbloccate.
appena si sono sbloccate a quel punto il Tablelock da il consenso e permette di eseguire le operazioni sui record delle tabelle specificate.

Grazie Max, ma quello che vorrei fare io bloccare il record che voglio aggiornare, non l’intera tabella.

puoi sempre aggiungere una colonna con ROWID e modificare un pochino la chiamata e la consultazione della classe di blocco.
se tabella -> idrecord bloccata allora attendi…

E’ la soluzione che mi avevano prospettato anche Antonio Rinaldi e JulianS, ma quello che io cercavo era una soluzione in cui la gestione del lock sul record fosse demandata e gestita dal motore del database, e che io dovessi solo segnalare la cosa all’utente. Se questo non possibile con ODBCDatabase per me non un grosso problema (per ora): non voglio mettere in piedi meccanismi che mi complicano la vita. Nel progetto che sto scrivendo gli utenti che lo utilizzeranno saranno due, per cui non ci sono molti rischi di concorrenzialit nell’aggiornamento dei dati. Pi che altro mi interessava sapere se quello che riuscivo a fare con ADO possibile farlo anche con ODBCDatabase.
Grazie comunque per i tuoi suggerimenti (fra l’altro sto utilizzando la tua procedura di generazione di codici fiscali e funziona alla grande!)

Io la soluzione la chiamo ‘Parapalle’ ;
Nel senso che comunque serve per evitare di avere problemi, a volte, anche grossi.
Alla fine una classe con poche righe di codice.

Controllando in giro … sui vari siti ( msdn, etc ) non vi sono soluzioni di lock a livello ODBC.
Se vuoi ti invio la classe che ho creato;
Tieni presente che ADO non lo ho mai digerito; con ODBC ho una convivenza di convenienza.
Sono sposato con Mysql, vado a letto con Postgree e di tanto in tanto mi ripasso un pochino SQLITE.
Con FILEMAKER abbiamo in corso una separazione legale.
Io e FOXPRO eravamo fidanzati una volta…

Ti ringrazio, Max. Se mi mandi la tua classe ci do un’occhiatina.
Io utilizzavo ADO perch con Visual Basic era praticamente la tecnica standard; ho utilizzato (e utilizzo tuttora) SQL Server; con Filemaker feci qualche anno fa un’applicazione per mio figlio, ma niente di pi (non molto adatto a gestire dati come dico io).
A MySql e FoxPro diedi un’occhiatina veloce tanto tempo fa.
SQLite mi piace perch semplice e non richiede installazioni di alcunch, anche se questo vuol dire avere uno strumento che difficilmente si presta alla multiutenza e che, probabilmente, in applicazioni remote non il massimo.

Grazie ancora, Max…e buona programmazione!