How to speed up sqllite transaction (write data)? - vb.net

I am currently self learning (2 weeks) vb with visual studio 2015 express.
I am trying to import selected data from an access database to a datagridview. I want to write that data inside sqllite database.
My code is shown below. It works but I am not sure if the transaction is set up correctly. Also I am not sure if I have to use a transaction when writing to the sqllite DB. The operation seems slow and I think that it could be quicker.
Do I have the transaction set up correctly?
My code is:
Dim con As String = "Data Source=C:\clients.db;Version=3;"
Dim sqConnection As New SQLiteConnection(con)
Dim sqCommand As New SQLiteCommand()
sqCommand.Connection = sqConnection
Dim myTrans As SQLiteTransaction '= sqConnection.BeginTransaction()
sqConnection.Open()
' Start a local transaction
myTrans = sqConnection.BeginTransaction()
' Assign transaction object for a pending local transaction
sqCommand.Transaction = myTrans
Try
sqCommand.CommandText = "INSERT INTO Clients (Registered, Name, Number, Details) VALUES (#Registered, #Name, #Number, #Details)"
sqCommand.Parameters.AddWithValue("#Registered", row.Cells(1).Value)
sqCommand.Parameters.AddWithValue("#Name", row.Cells(2).Value)
sqCommand.Parameters.AddWithValue("#Number", row.Cells(3).Value)
sqCommand.Parameters.AddWithValue("#Details", row.Cells(4).Value)
sqCommand.ExecuteNonQuery()
myTrans.Commit()
Application.DoEvents()
Finally
sqConnection.Close()
End Try
I have looked at various questions/replies which left me even more confused. I am getting nowhere fast and seem to be going in circles.

You do not have to use a transaction to add a record to the database. I would use a transaction when you are doing something that preforms multiple operations on the database. If for some reason something goes wrong during the process the transaction can roll back the changes to the database

Resolved I moved the myTrans.Commit() to just before the End Sub and now the insert is twice as fast..

Related

Updating Access Database with Visual Basic(2017) textfield

I'm working on a simple billing program for my father in law and the Access database is not updating. I built the connection and query inside of a try catch block but the database still won't update outside of it. I know it's probably something simple but I just can't see it. What am I missing?
'Database Connection
Dim Connection As String = "Provider=Microsoft.Jet.OLEDB.4.0; User ID=Admin; Data Source =MBPDatabase.mdb;"
Dim MBP As New OleDb.OleDbConnection(Connection)
MBP.Open()
Dim sqlJob As String
sqlJob = "INSERT INTO Jobs ([jobID], [jobClients], [jobName], [jobPrice])
VALUES("",#jobClients,#jobName,0.00)"
Dim Job As New OleDb.OleDbCommand(sqlJob, MBP)
Job.Parameters.Add(New OleDb.OleDbParameter("jobClients", txtClient.Text))
Job.Parameters.Add(New OleDb.OleDbParameter("jobName", txtJobName.Text))
Job.ExecuteNonQuery()
MBP.Close()
The problem is most likely the double qoute for jobID after the VALUES-keyword.
This leads to the following result (one double qoute):
...VALUES(",#jobClients,#jobName,0.00)
Change it to this (two single quotes):
sqlJob = "...VALUES('',#jobClients,#jobName,0.00)"

How to catch concurrency exception for a shared local database (SQL Server Compact | .sdf) in Visual Basic?

I am writing a programm that allows two users to access the same local database (.sdf) in a shared folder. The database consists of tasks for the employee. One user (Master) adds and edits those tasks, the other one (Slave) marks them as done/undone and updates the list.
The two of them should be allowed to work simultaneously. I.e. Master adds a new task, Slave finishes one task, updates the list and finds the new one immediately.
When a database query is executed an instance of a module is created that opens the database, executes the SQL-query (Select, Update, Delete, ...) and closes it. This is how the VB module looks like:
Public Sub ExecQuery(Query As String)
Try
SQLCon.Open()
SQLCmd = New SqlCeCommand(Query, SQLCon) 'Create SQL command
' Load Parameters into SQL Command
Params.ForEach(Sub(x) SQLCmd.Parameters.Add(x))
Params.Clear() ' Clear Parameter List
' Execute Command, Fill Dataset and close database
SQLDS = New DataSet
SQLCon.Close()
Catch ex As Exception
Exception = ex.Message
End Try
If SQLCon.State = ConnectionState.Open Then SQLCon.Close()
End Sub
If have to admit, that I took the code-snippet from a tutorial.
Now an update of the DataGridView would look like this:
Public Sub GetEntry()
Dim ii As Integer = 0
grdData.Rows.Clear() 'empty GridDataView
SQL.ExecQuery("SELECT * FROM Tasks)
grdData.Rows.Add(n)
For Each r As DataRow In SQL.SQLDS.Tables(0).Rows
grdData.Item(0, ii).Value = r(0)
grdData.Item(1, ii).Value = r(1)
grdData.Item(2, ii).Value = r(2)
' ...
ii += 1
Next
If SQL.Exception <> "" Then
MsgBox(SQL.Exception)
End If
End Sub
The problem is that when Master and Slave both perform an SQL.ExecQuery(String) command, they are accessing the same .sdf which should not be possible, right? I expected something like "Cannot open database.sdf, as it is currently accessed by another user". I then would like to let the script wait for one second and retry. But instead I get "Error: Index out of range". So I assume, the code tries to read all entries but it can't.
Does anybody know how to catch this exception? I read about a
Data.DBConcurrencyException
but I don't know if it's the thing I am looking for and how to use it.

VB.NET - Transaction rollback

What happens if a transaction is neither committed nor rolled back. I am looking for answers for SQL Server and/or Oracle. Please see the code below:
Public Sub TransactionTest()
Try
Dim intCount As Integer
Dim sql As SqlTransaction
Dim objCon As New SqlConnection("Data Source=IANSCOMPUTER;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=true")
objCon.Open()
Dim trans As SqlTransaction
trans = objCon.BeginTransaction
Dim paramValues(0) As SqlParameter
paramValues(0) = New SqlParameter("#ID", 1)
Using (objCon)
intCount = SqlHelper.ExecuteNonQuery(trans, CommandType.Text, "UPDATE person SET URN=1 WHERE ID2=#ID", paramValues)
paramValues(0) = New SqlParameter("#ID", 2)
intCount = SqlHelper.ExecuteNonQuery(trans, CommandType.Text, "UPDATE person SET URN=2 WHERE ID2=#ID", paramValues)
paramValues(0) = New SqlParameter("#ID", 3)
intCount = SqlHelper.ExecuteNonQuery(trans, CommandType.Text, "UPDATE person SET URN=3 WHERE ID2=#ID", paramValues)
End Using
Catch ex As Exception
'I do not swallow transactions
End Try
End Sub
Notice that it is neither committed nor rolled back. In the case above it appears to roll back.
I have noticed that on my SQL Server 2005 Express server at home that SQL Studio Manager hangs when running a query directly in the console until the program above finishes. In my work environment this is not the case i.e. you can run queries simultaneously. Is this because of the isolation level? Therefore I have two questions:
What happens if a transaction is neither committed nor rolled back. I have read articles like this: What happens if you don't commit transaction in a database (say SQL Server). Can I assume that the transaction is rolled back in SQL Server and Oracle?
Why does SQL server hang in one environment when a transaction is active and in another environment it does not?
I am looking specifically for an anwer to question 2.
If you don't commit the transaction, the database will roll it back when you close the connection, assuming something bad happened to your code. This is how any serious database behaves.
As for your second question, I guess it has to do with locking, but it's quite hard to say without knowing more about your environments and who uses them.

Possible connection leaking causing "System.Data.SqlClient.SqlException: Timeout expired" error in SQL Server?

My application requires a user to log in and allows them to edit a list of things. However, it seems that if the same user always logs in and out and edits the list, this user will run into a "System.Data.SqlClient.SqlException: Timeout expired." error. I've read a comment about it possibly caused by uncommitted transactions. And I do have one going in the application.
I'll provide the code I'm working with and there is an IF statement in there that I was a little iffy about but it seemed like a reasonable thing to do.
I'll just go over what's going on here, there is a list of objects to update or add into the database. New objects created in the application are given an ID of 0 while existing objects have their own ID's generated from the DB. If the user chooses to delete some objects, their IDs are stored in a separate list of Integers. Once the user is ready to save their changes, the two lists are passed into this method. By use of the IF statement, objects with ID of 0 are added (using the Add stored procedure) and those objects with non-zero IDs are updated (using the Update stored procedure). After all this, a FOR loop goes through all the integers in the "removal" list and uses the Delete stored procedure to remove them. A transaction is used for all this.
Public Shared Sub UpdateSomethings(ByVal SomethingList As List(Of Something), ByVal RemovalList As List(Of Integer))
Using DBConnection As New SqlConnection(conn)
DBConnection.Open()
Dim MyTransaction As SqlTransaction
MyTransaction = DBConnection.BeginTransaction()
Try
Using MyCommand As New SqlCommand()
MyCommand.Transaction = MyTransaction
MyCommand.CommandType = CommandType.StoredProcedure
For Each SomethingItem As Something In SomethingList
MyCommand.Connection = DBConnection
If SomethingItem.ID > 0 Then
MyCommand.CommandText = "UpdateSomething"
Else
MyCommand.CommandText = "AddSomething"
End If
MyCommand.Parameters.Clear()
With MyCommand.Parameters
If MyCommand.CommandText = "UpdateSomething" Then
.Add("#id", SqlDbType.Int).Value = SomethingItem.ID
End If
.Add("#stuff", SqlDbType.Varchar).Value = SomethingItem.Stuff
End With
MyCommand.ExecuteNonQuery()
Next
MyCommand.CommandText = "DeleteSomething"
For Each ID As Integer In RemovalList
MyCommand.Parameters.Clear()
With MyCommand.Parameters
.Add("#id", SqlDbType.Int).Value = ID
End With
MyCommand.ExecuteNonQuery()
Next
End Using
MyTransaction.Commit()
Catch ex As Exception
MyTransaction.Rollback()
'Exception handling goes here '
End Try
End Using
End Sub
There are three stored procedures used here as well as some looping so I can see how something can be holding everything up if the list is large enough.
I'm using Visual Studio 2008 to debug and am using SQL Server 2000 for the DB.
Edit: I still seem to be getting this error. I've even removed the whole transaction thing and I still encounter it. At this point, I'm assuming there is some kind of leak happening here. I've tried not using the USING statements and explicitly tell the command and connection to dispose itself but no dice. Memory usage by SQL Server also increases quite a bit if this method is called a lot in a short period of time.
I've read that increasing the CommandTimeout property of the SQLCommand would help. I'm wondering if there are any big disadvantages or consequences from doing so.
I would suggest using the following, that way Dispose will always be called and be Rolledback in every non-committed case.
using (SqlConnection sqlCn = new SqlConnection())
{
using (SqlTransaction myTrans = sqlCn.BeginTransaction())
{
...
myTrans.Commit();
}
}
Also, I don't believe you need to make a new SqlCommand for every execution. Just maintain the same one and update the CommandText and Parameters.
If you have a large number of commands, you may want to build them all before opening the connection. After you start the transaction and open the connection, spin through and execute them.
You probably want to use TransactionScope
Using _tx as New System.Transactions.TransactionScope(<add your own timeout here>)
'Do all your sql work'
If _noErrors Then
_tx.Complete()
End If
End Using
With the transaction scope, you can set a timeout of up to 20 minutes without modifying server settings.
I believe I have managed to solve the problem. I have modified the application so that unnecessary calls to the database are not made (i.e. unchanged objects do not need to be updated again) and increased the CommandTimeout property for the SQLCommand object. So far, no problems.
Big thanks for suggestions too.

Connect an SQL database to a DataGridView with separate command buttons?

I'm a bit puzzled here. I did a form containing a simple datagridview containing data from an MDB file. The connection was alltogether done by Visual Studios wizard so alot of stuff was created automatically. Then I databinded the textboxes to each column in the database and managed to update the database via my own command buttons such as "Update" with this code:
Me.MainTableBindingSource.EndEdit()
Me.MainTableTableAdapter.Update(Me.DBDataSet.MainTable)
Me.DBDataSet.MainTable.AcceptChanges()
This doesn't seem to work with sql. At this point, I've done everything from scratch in this order, added a datagridview and added a database connection with the wizard when connecting the datagridview to a database. Only this time around I created an SQL connection instead.
And Visual Studio created "MainTableTableAdapter", "DBDataSet", "DBDataSet.MainTable".
The SQL server is something which was installed automatically when installing Visual Studio and it does seem to work after creating the table adapter and dataset if there's data in it. In some time I plan to use an SQL Server on the internet which hosts the dataset. So I want to be able to easily edit the source.
The only thing missing now is how to add a row, delete selected row and editing selected row via my own textboxes. More like a fill-in form. Any ideas to put me in the right direction? I've tried googling it and I've found some stuff, but most of it contains stuff on how to create the datasets and etc. And I'm not sure what Visual Studio has done automatically. And I want to update everything just as easily as I did with these three lines of code when I used a local MDB file.
If it's SQL you can do something like this. Here's an example delete function:
Public Shared Function DeleteStuff(ByVal id As Integer) As Integer
Dim query As String = _
"DELETE FROM tbl_Stuff " & _
"WHERE ID_Stuff = " & id & ";"
Dim connection As SqlConnection = YourDB.GetConnection
Dim deleteCommand As New SqlCommand(query, connection)
Dim rowCount As Integer = 0
Try
connection.Open()
rowCount = deleteCommand.ExecuteNonQuery()
Catch ex As SqlException
Throw ex
Finally
connection.Close()
End Try
Return rowCount
End Function
There may be better ways, but you can always pass in SQL queries.
EDIT: Sorry this is more than three lines of code. ;)