I have a large number of records to add to a Access database in realtime. The items are temperatures, water flows etc from electronic instruments.
The code I have is below, can it be improved or is there a different way to add records more quickly - currently, 10,000 records takes approx. 10secs.
I am currently simulating the data to prove the concept of adding large number of records.
Dim connetionString As String
Dim connection As OleDbConnection
Dim oledbAdapter As New OleDbDataAdapter
Dim sql As String
connetionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Neil Cooley\Desktop\Database1.accdb"
connection.Open()
Dim stop_watch_1 As New Stopwatch
Dim getDate As Date = Date.Now
For i As Integer = 1 To 10000 Step 1
sql = "INSERT INTO Lines VALUES('TAG " & i.ToString & "','" & i.ToString & "','192','" & getDate & "')"
oledbAdapter.InsertCommand = New OleDbCommand(sql, connection)
oledbAdapter.InsertCommand.ExecuteNonQuery()
Next
stop_watch_1.Stop()
MsgBox("Row(s) Inserted !! - " & stop_watch_1.ElapsedMilliseconds.ToString)
I think that the best you can hope for with that is to use SQL parameters, like this:
Dim connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Neil Cooley\Desktop\Database1.accdb"
Dim sql = "INSERT INTO Lines([tag], [num], [x], [dt]) VALUES(?, ?, '192', ?)"
Dim stop_watch_1 As New Stopwatch()
Using conn As New OleDbConnection(connString),
cmd As New OleDbCommand(sql, conn)
cmd.Parameters.Add(New OleDbParameter With {.ParameterName = "#tag", .OleDbType = OleDbType.VarChar, .Size = 20})
cmd.Parameters.Add(New OleDbParameter With {.ParameterName = "#num", .OleDbType = OleDbType.VarChar, .Size = 20})
cmd.Parameters.Add(New OleDbParameter With {.ParameterName = "#date", .OleDbType = OleDbType.Date})
cmd.Parameters("#date").Value = DateTime.UtcNow()
conn.Open()
For i As Integer = 1 To 10000 Step 1
cmd.Parameters("#tag").Value = "TAG " & i.ToString()
cmd.Parameters("#num").Value = i.ToString()
cmd.ExecuteNonQuery()
Next
End Using
stop_watch_1.Stop()
MsgBox("Row(s) Inserted !! - " & stop_watch_1.ElapsedMilliseconds)
Make sure to set the data type and sizes of the parameters to match the database columns. You should declare the column names in the INSERT statement. The parameters are represented by ? in the query; they are added in the order they are used. The parameter names in the OleDbParameter instances are for convenience.
(Make sure to use a DateTime type in the database for DateTimes, and keeping it in UTC avoids daylight savings time change problems.)
Using a faster disk drive could help, perhaps one that the OS is not run from.
If using a different database is an option, perhaps a key-value store would serve you well. Or even just a plain CSV file.
I would try using a data table - it should run faster.
Say, try like this:
Dim sqlCon As New OleDbConnection(My.Settings.AccessDB)
Dim strSql As String = "SELECT * FROM tblBig WHERE ID = 0"
Dim cmdSQL As New OleDbCommand(strSQL, sqlCon)
Dim rstData = New DataTable
sqlCon.Open()
rstData.Load(cmdSQL.ExecuteReader)
Dim da As New OleDbDataAdapter(cmdSQL)
Dim daU As New OleDbCommandBuilder(da)
Dim stop_watch_1 As New Stopwatch
Dim getDate As Date = Date.Now
stop_watch_1.Start()
For i As Integer = 1 To 10000
Dim MyNewRow = rstData.NewRow
MyNewRow("City") = "City" & i
MyNewRow("FirstName") = "FirstName " & i
MyNewRow("LastName") = "LastName " & i
rstData.Rows.Add(MyNewRow)
If (i Mod 500) = 0 Then
'Debug.Print("data write" & stop_watch_1.ElapsedMilliseconds / 1000)
da.Update(rstData)
rstData.Clear()
End If
Next
' write data back to database
da.Update(rstData)
sqlCon.Close()
stop_watch_1.Stop()
MsgBox("Row(s) Inserted !! - " & stop_watch_1.ElapsedMilliseconds / 1000)
I have it set to write out in above every 500 rows.
However, you are getting about 1,000 rows per second, and I not at all sure you going to increase that rate - it is VERY good rates you already seeing, and I can't say the above is going to help much.
edit: I feel the need for speed!!! SUPER FAST!!!
Ok, if you want to bring this down from 10 seconds to 1/3 of a second?
by-pass the .net providers. Hit the database DIRECT with the native DAO object library. You get about 30 times faster.
So, try this:
Imports Microsoft.Office.Interop.Access
and then do this:
Dim dbE As New Dao.DBEngine
Dim db As Dao.Database
db = dbE.OpenDatabase("C:\test\test44.accdb")
Dim rstData As Dao.Recordset
rstData = db.OpenRecordset("tblBig")
Dim stop_watch_1 As New Stopwatch
stop_watch_1.Start()
For i As Integer = 1 To 10000
rstData.AddNew()
rstData("City").Value = "City" & i
rstData("FirstName").Value = "FirstName " & i
rstData("LastName").Value = "LastName " & i
rstData.Update()
Next
db.Close()
stop_watch_1.Stop()
MsgBox("Row(s) Inserted !! - " & stop_watch_1.ElapsedMilliseconds / 1000)
You find a min of 30x faster - even more. So, DIRECT use of the data engine, and by-passing all of the .net providers? You EASY insert 10,000 rows in WELL UNDER 1 second.
Related
I am trying to compare the values in my dictionary which contains values/quantities to a standard database containing similar values/quantities. I want to check if the value matches the database, then check if the quantity is less than the database.
I keep getting an error when I run the code on the "Using myreader" line, with the following: System.Data.OleDb.OleDbException: 'IErrorInfo.GetDescription failed with E_FAIL(0x80004005).' This is my first time using OleDBDatareader so perhaps I am doing something incorrectly. Is this not allowed, I assumed comparing a list a values in a dictionary should be possible.
Dim myconnection As OleDbConnection
Dim constring As String = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=C:\UsersTables.accdb; Persist Security Info=False"
myconnection = New OleDbConnection(constring)
myconnection.Open()
Dim keepSell As String
For Each KeyPair In colSums
Dim sqlQry As String
sqlQry = "SELECT Part,Quantity FROM PartsList WHERE Item = '" & keyPair.Key & "'AND Size= '" & keyPair.Value & "'"
Dim cmd As New OleDbCommand(sqlQry, myconnection)
Using myreader As OleDbDataReader = cmd.ExecuteReader()
If myreader.Read() Then
keepSell = "Keep"
Else
keepSell= "Sell"
End If
'myreader.Close()
End Using
DataGridView1.Rows.Add(KeyPair.Key, KeyPair.Value, keepSell)
Next
My code for updating mysql table having more than 2000 rows through a loop using VB.net is working fine but too slow. Is there any way to update it faster ? Anybody please help. Thanks. My code is given below.
Dim mysqlconn = New MySqlConnection
mysqlconn.ConnectionString = "server=localhost;user id=root;password=1234;database=Share"
mysqlconn.Open()
Dim adapter As New MySqlDataAdapter("SELECT * FROM name_list;", mysqlconn)
Dim datatable As New DataTable()
adapter.Fill(datatable)
Dim cmd As New MySqlCommand
cmd.Connection = mysqlconn
Dim sql As String
Dim i as integer = 0
While i <= datatable.Rows.Count - 1
Dim sy As String = datatable.Rows(i).Item(3).ToString.Trim
sql = "UPDATE Name_list Set Numerology = '" & "N-" & variable1 & " S- " & variable2 & "',FSTLetter = '" & variable3 & "',Timing = '" & vriable4 & "',P_Numerology = '" & variable5 & "' WHERE Symbol = '" & sy & "'"
sy = ""
cmd.CommandText = sql
cmd.ExecuteNonQuery()
i = i + 1
End While
adapter.Fill(datatable)
DataGridView1.DataSource = datatable
DataGridView1.Refresh()
Don't name variables the same as class names (DataTable, datatable) vb.net is case insensitive so it confuses intellisense.
Database connections and commands need to closed and disposed.
Using blocks do this for you even if there is an error.
You can set the connection string by passing it directly to the constructor of the connection. Likewise, you can set the command text and the connection by passing to the constructor of the command.
Don't open the connection until right before it is used. In this case we don't need a DataAdapter but if you are using one it is not necessary to open the connection at all. The .Fill method of the DataAdapter will open and close the connection. However if the DataAdapter finds an open connection it will leave it open.
To avoid confusion, I created 2 data tables. I used a single connection but I opened and closed it each time it is used. It is not disposed until the last End Using.
The first command retrieves only the sy column. Replace Column4_Name with the actual column name. We don't want to pull down data we don't need so no Select *.
Always use parameters to avoid sql injection which can damage your database. You need to check your database for the types of the fields. I had to guess. Each parameters value is set except sy which changes on each iteration.
No need to close the connection after the last Select command since the final End Using will close and dispose.
After the connection is closed we update the user interface.
Private Sub OPCode(variable1 As String, variable2 As String, variable3 As String, variable4 As String, variable5 As String)
Dim dtBeforeUpdate As New DataTable()
Dim dtAfterUpdate As New DataTable()
Using mysqlconn = New MySqlConnection("server=localhost;user id=root;password=1234;database=Share")
Using cmd As New MySqlCommand("SELECT Column4_Name FROM name_list;", mysqlconn)
mysqlconn.Open()
dtBeforeUpdate.Load(cmd.ExecuteReader)
mysqlconn.Close()
End Using
Using cmd As New MySqlCommand($"UPDATE Name_list Set Numerology = #Numerologh, FSTLetter = #FST, Timing = #Timing, P_Numerology = #P_Numerology WHERE Symbol = #sy ", mysqlconn)
cmd.Parameters.Add("#Numerology", MySqlDbType.String).Value = $"N-{variable1} S- {variable2}"
cmd.Parameters.Add("#FST", MySqlDbType.String).Value = variable3
cmd.Parameters.Add("#Timing", MySqlDbType.String).Value = variable4
cmd.Parameters.Add("#P_Numerology", MySqlDbType.String).Value = variable5
cmd.Parameters.Add("#sy", MySqlDbType.String)
mysqlconn.Open()
For Each row As DataRow In dtBeforeUpdate.Rows
cmd.Parameters("#sy").Value = row(0).ToString.Trim
cmd.ExecuteNonQuery()
Next
mysqlconn.Close()
End Using
Using cmd As New MySqlCommand("Select * From Name_list;", mysqlconn)
mysqlconn.Open()
dtAfterUpdate.Load(cmd.ExecuteReader)
End Using
End Using
DataGridView1.DataSource = dtAfterUpdate
End Sub
Actually, I don't get it. It seems like you are updated the entire table with the same data. Do you expect the variable1, variable2 etc. to change somehow?
Don't know where your variable1, variable2, etc come from, but those look to be static, so your loop is somewhat pointless it would seem. You're updating every row in the loop to the exact same values (unless there's other code you're not showing), so just update the table without the loop:
Using con As New MySQLConnection
con.ConnectionString = "server=localhost;user id=root;password=1234;database=Share"
con.Open
Using cmd As New MySQLCommand
cmd.Connection = con
cmd.CommandText = "UPDATE Name_list Set Numerology = '" & "N-" & variable1 & " S- " & variable2 & "',FSTLetter = '" & variable3 & "',Timing = '" & vriable4 & "',P_Numerology = '" & variable5 & "'"
cmd.ExecuteNonQuery
End Using
End Using
cmd.CommandText = (Convert.ToString("SELECT * From [") & excelSheet) & "] Order By" & columnCompany & "OFFSET 0 ROWS FETCH NEXT 100000 ROWS ONLY "
I am trying to map data form excel to an sql table but the excel table has a lot of records in it, so I run out of memory if I try to add all of the records at once. So what I am trying to do is use this line of code
cmd.CommandText = (Convert.ToString("SELECT * From [") & excelSheet) & "] Order By B OFFSET 0 ROWS FETCH NEXT 100000 ROWS ONLY "
So that 100000 row will be added. Then I intend to clear the data table and do the same only from 100001 to 200000 and so on.
But I cant get this to work correctly, it works fine when it is just SELECT TOP 100000 FROM... but then I dont know how to get the next 100000 record. I think the Order By is where the issue is as I'm not sure how to get the correct column.
Here is my code Below
Private Sub btnMap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnMap.Click
'Allows columns to be moved mapped in the correct order
Dim nbColumnsToTransfer As Integer = dgvMapped.Columns.GetColumnCount(1)
Dim indexes As List(Of Integer) = (From column As DataGridViewColumn In dgvLoadedata.Columns.Cast(Of DataGridViewColumn)() _
Take nbColumnsToTransfer _
Order By column.DisplayIndex _
Select column.Index).ToList()
Dim columnCompany As Integer = indexes(0)
Dim columnEmail As Integer = indexes(1)
Dim columnFirstName As Integer = indexes(2)
Dim columnSurname As Integer = indexes(3)
Dim columnPostCode As Integer = indexes(4)
Dim columnTelephone As Integer = indexes(5)
Dim columnEmployeeBand As Integer = indexes(6)
Dim DeleteConnection As New Data.SqlClient.SqlConnection
DeleteConnection.ConnectionString = "Data Source=DC01\SQLEXPRESS;Initial Catalog=YCM;User ID=sa;Password=S0rtmypc!"
DeleteConnection.Open()
Dim sqlDTM As String = "Delete From TempMapped"
Dim command As New SqlCommand(sqlDTM, DeleteConnection)
command.ExecuteNonQuery()
Dim sqlDTSR As String = "Delete From TempSplitReq"
command = New SqlCommand(sqlDTSR, DeleteConnection)
command.ExecuteNonQuery()
DeleteConnection.Close()
If chkContactSplitReq.Checked = True Then
Dim dt As New DataTable()
Using con As New OleDbConnection(conString)
Using cmd As New OleDbCommand()
Using oda As New OleDbDataAdapter()
cmd.CommandText = (Convert.ToString("SELECT * From [") & excelSheet) & "] Order By" & columnCompany & "OFFSET 0 ROWS FETCH NEXT 100000 ROWS ONLY ""
cmd.Connection = con
con.Open()
oda.SelectCommand = cmd
oda.Fill(dt)
con.Close()
End Using
End Using
End Using
Dim connection As String = "Data Source=DC01\SQLEXPRESS;Initial Catalog=YCM;User ID=sa;Password=S0rtmypc!"
Using cn As New SqlConnection(connection)
cn.Open()
Using copy As New SqlBulkCopy(cn)
copy.ColumnMappings.Add(columnCompany, 0)
copy.ColumnMappings.Add(columnEmail, 1)
copy.ColumnMappings.Add(columnFirstName, 2)
copy.ColumnMappings.Add(columnPostCode, 3)
copy.ColumnMappings.Add(columnTelephone, 4)
copy.DestinationTableName = "TempSplitReq"
copy.WriteToServer(dt)
End Using
End Using
Dim SplitConnection As New Data.SqlClient.SqlConnection
SplitConnection.ConnectionString = "Data Source=DC01\SQLEXPRESS;Initial Catalog=YCM;User ID=sa;Password=S0rtmypc!"
SplitConnection.Open()
Dim sqlSplit As String = "TempSplitReq_SaveToMapped"
Dim commandSplit As New SqlCommand(sqlSplit, SplitConnection)
commandSplit.ExecuteNonQuery()
SplitConnection.Close()
Else
Dim dt As New DataTable()
Using con As New OleDbConnection(conString)
Using cmd As New OleDbCommand()
Using oda As New OleDbDataAdapter()
cmd.CommandText = (Convert.ToString("SELECT * From [") & excelSheet) + "] "
cmd.Connection = con
con.Open()
oda.SelectCommand = cmd
oda.Fill(dt)
con.Close()
End Using
End Using
End Using
Dim connection As String = "Data Source=DC01\SQLEXPRESS;Initial Catalog=YCM;User ID=sa;Password=S0rtmypc!"
Using cn As New SqlConnection(connection)
cn.Open()
Using copy As New SqlBulkCopy(cn)
copy.ColumnMappings.Add(columnCompany, 0)
copy.ColumnMappings.Add(columnEmail, 1)
copy.ColumnMappings.Add(columnFirstName, 2)
copy.ColumnMappings.Add(columnSurname, 3)
copy.ColumnMappings.Add(columnPostCode, 4)
copy.ColumnMappings.Add(columnTelephone, 5)
copy.DestinationTableName = "TempMapped"
copy.WriteToServer(dt)
End Using
End Using
End If
MsgBox("Data Mapping Complete")
End Sub
A SqlBulkCopy object has a BatchSize property. I have a similar situation and set
copy.BatchSize = 1000
And mine works with a large spreadsheet import. Adjust this property in your environment for better performance.
still very new to this and can't seem to find exactly what I'm looking for. Quick run-through on what I'm trying to accomplish. I have a datagridview (3 columns - Id, Name, Address) that is connected to a local .mdf database file, that I'm able to search through using a search textbox. My goal NOW is to submit records into the database directly using 2 text fields and the Id field to automatically increment. (Id++, txtName.Text, txtAddress.Text) and to use a send button(btnSend) to activate this event.(PLEASE KEEP IN MIND, MY GOAL IS TO HAVE EVERYONE INCLUDING THE NEW RECORD SHOW UP IN THE DATAGRIDVIEW AND FOR THE NEW ROW TO BE INSERTED DIRECTLY TO THE DATABASE AND SAVE ANY CHANGES) I've been hammering at this for a couple days now and would appreciate any help. Below is my code, but please keep in mind I'm still new and trying to figure this language out so if there's any unnecessary code, please do let me know... Also if you want to help with one additional thing, maybe some code on how to export that table to a different file from an export button. Thanks! I'm currently also getting an error saying "Cannot find table 0." when I click the btnSend button.
Public Sub btnSend_Click(ByVal sender As Object, e As EventArgs) Handles btnSend.Click
Try
Dim connectionString As String
Dim connection As SqlConnection
Dim ds As New DataSet("Table")
Dim dataset As New DataSet()
Dim sqlInsert As String
Dim sqlSelect As String
Dim Id As Integer = 5
Dim newRow As DataRow = dataset.Tables(0).NewRow()
connectionString = "Data Source=(LocalDB)\v11.0;AttachDbFilename=""" & My.Application.Info.DirectoryPath & "\Database1.mdf"";Integrated Security=True;"
sqlInsert = "INSERT INTO Table (#Id, #Name, #Address) VALUES (" & Id & ", '" & txtName.Text & "','" & txtAddress.Text & "')"
sqlSelect = "SELECT * FROM Table"
connection = New SqlConnection(connectionString)
Dim da As New SqlDataAdapter()
connection.Open()
da.Fill(ds)
Using da
da.SelectCommand = New SqlCommand(sqlSelect)
da.InsertCommand = New SqlCommand(sqlInsert)
da.InsertCommand.Parameters.Add(New SqlParameter("Id", SqlDbType.Int, 4, Id))
da.InsertCommand.Parameters.Add(New SqlParameter("Name", SqlDbType.NText, 50, txtName.Text))
da.InsertCommand.Parameters.Add(New SqlParameter("Address", SqlDbType.NText, 50, txtAddress.Text))
Using dataset
da.Fill(dataset)
newRow("Id") = Id
newRow("Name") = txtName.Text
newRow("Address") = txtAddress.Text
dataset.Tables(0).Rows.Add(newRow)
da.Update(dataset)
End Using
Using newDataSet As New DataSet()
da.Fill(newDataSet)
End Using
End Using
connection.Close()
Catch ex As Exception
MsgBox(ex.Message)
Throw New Exception("Problem loading persons")
End Try
Dim updatedRowCount As String = gvDataViewer.RowCount - 1
lblRowCount.Text = "[Total Row Count: " & updatedRowCount & "]"
End Sub
I need to show the data from the column 'Purchaser' starting with the text entered in the textbox 'Purchaser' on the form. I am using MS Access 2003 database.
For this I am using the following...
Dim query = "SELECT * FROM Details WHERE [Purchaser] LIKE '" & Purchaser.Text & "*'"
Dim dc = New OleDbCommand(query, cn)
Dim rows = dc.ExecuteNonQuery
cn.Close()
If rows = 0 Then
'Show a form for new entry
Else
Dim oleadap = New OleDbDataAdapter(query, cn)
Dim dset As DataSet = Nothing
oleadap.Fill(dset, "Details")
For i = 0 To rows
Dim purName = dset.Tables("Details").Rows(i).Item("Purchaser").ToString
Dim purAddr = dset.Tables("Details").Rows(i).Item("Address").ToString
'Populate a list
Next
End If
The variable 'rows' always turns out to be zero even if I check for a Purchaser starting with, say A, in the database.
That should be:
Dim query = "SELECT * FROM Details WHERE [Purchaser] LIKE '" _
& Purchaser.Text & "%'"
In MS Access, the wild card is asterisk (*), outside of it, the wildcard is percent (%)
In addition, you have ExecuteNonQuery, but that is not true. You are executing a query, here are a few notes for testing.
Dim query = "SELECT * FROM Details WHERE [Purchaser] LIKE '" _
& Purchaser.Text & "%'"
Dim dc = New OleDbCommand(query, cn)
Dim rows As OleDb.OleDbDataReader
rows = dc.ExecuteReader
If rows.HasRows Then
Do While rows.Read()
Console.WriteLine(rows("Purchaser"))
Loop
End If
Console.ReadLine()
Can you use % instead of *. And another one, use parameter.
Dim query = "SELECT * FROM Details WHERE [Purchaser] LIKE #purc & '%' "
Dim dc = New OleDbCommand(query, cn)
dc.Parameters.AddWithValue("#purc", Purchaser.Text)
Dim rows = dc.ExecuteNonQuery