Easiest way to update a OleDB table from a DataTable - vb.net

What's the easiest way (vb.NET) to update a full OleDB table from a previous modified imported table?
Here's what I have.
First I import the table from an Access Database:
Dim PRDB As String = "Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=<location>;
Jet OLEDB:Database Password=<password>"
Dim CNDB As New OleDb.OleDbConnection(PRDB)
Dim CMDB As New OleDb.OleDbCommand
Dim ADDB As New OleDb.OleDbDataAdapter(CMDB)
Dim TBDB As New DataTable
Try : CNDB.Open()
Catch EX As Exception : MsgBox(EX.ToString) : End Try
Try : CMDB.CommandText = "SELECT * FROM [TABLE 01]" : ADDB.Fill(TBDB)
Catch EX As Exception : MsgBox(EX.ToString) : End Try
So far so good. I managed to import TABLE 01 successfully to TBDB.
Now, let's say I change one cell of the DataTable:
TBDB.Rows(2).Item(3) = "CHANGED"
Is there a way to update directly the OleBD table without describing the changed cell adress? Something like
ADDB.Update(TBDB) (?)
I already tried the last bit of code but it didn't work (I think it works only if the argument is a DataRow not a DataTable).
I researched this matter, but all the answers I found looked a lit bit complicated for this (apparent) simple task. So I wonder if there is a more direct way to do this - I also read something about Binders, but I didn't quite understand the method.
Thanks

Thanks to #Fadi, I managed to get the exact method I was looking for using the OleDbCommandBuilder:
Dim CMNDBUID As OleDb.OleDbCommandBuilder
CMNDBUID = New OleDb.OleDbCommandBuilder(ADDB)
CMNDBUID.GetUpdateCommand()
Then I can easily update the Access table:
Try : ADDB.Update(TBDB)
Catch EX As Exception : MsgBox(EX.ToString) : End Try

In SQL SERVER the equivalent is:
Private Sub UpdateDBFromDataTable(myDataTable As DataTable)
'Change with your connection string
Dim connectionString As String = "Data Source = MyServerName/Instance; Integrated Security=true; Initial Catalog=YourDatabase"
Using connection As SqlConnection = New SqlConnection(connectionString)
connection.Open()
Using bulkCopy As SqlBulkCopy = New SqlBulkCopy(connection)
For Each c As DataColumn In myDataTable.Columns
bulkCopy.ColumnMappings.Add(c.ColumnName, c.ColumnName)
Next
'Put here your table name in Database
bulkCopy.DestinationTableName = myDataTable.TableName
Try
bulkCopy.WriteToServer(myDataTable)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Using
End Using
End Sub

Related

OleDbDataAdapter doesn't like LIKE conditions

I have some code to find the first DataRow in some DB Table for a matching WHERE condition and an ORDER BY sort order which looks like:
Public Function GetRow(SQL As String) As DataRow
If Not OpenConnection() Then Return Nothing
Dim DT As New DataTable With {
.Locale = Globalization.CultureInfo.InvariantCulture
}
Dim Cmd As New OleDbCommand(SQL, Connection, Transaction)
Try
Using Adapter As New OleDbDataAdapter With {.SelectCommand = Cmd}
Adapter.Fill(DT)
End Using
Catch Ex As OleDbException
MsgBox(Ex.Message)
Finally
CloseConnection()
End Try
If DT.Rows.Count = 0 Then
Return Nothing
Else
Return DT.Rows(0)
End If
End Function
Assume this is part of a class, so the functions OpenConnection and CloseConnection do what their names are promising. This also applies to the Variables Connection and Transaction. The Catch part is only temporarily in it to test if there is an exception. But there wasn't.
Then I have an Access Database with a Table named 'Players' and some guys in it, especially a guy named 'Fred Bauer', which fills the colums Name and Forename like Name:=Bauer, Forename:=Fred (just as example). Now I like to search for the first guy which name begins with B, so the SQL looks like:
SELECT * FROM Players WHERE [Name] LIKE 'B*' ORDER BY [Name]
And the code finds nothing. DT.Rows.Count will be 0. Hmmmmm?! I know that the guy is in there, the SQL should have found him. To test the correctness of the code I tried the same SQL Code directly in Access as a view and it works fine. It returns a view with Fred Bauer as one row.
Then I tried to make the counter test and find the guy directly with my code and with:
SELECT * FROM Players WHERE [Name] = 'Bauer' ORDER BY [Name]
This works fine. The correct DataRow is found and given back. WHAT?!!
It seems like the OleDbDataAdapter doesn't like LIKE conditions. Maybe someone can help me out and get it work also for LIKE conditions? Or if you have any suggestions to do it in a complete different way, it's welcome.
You shouldn't be using an OpenConnection and CloseConnection methods. Connections and other database objects should be local to the method where they are used so they can be disposed. There is nothing in this code that requires a transaction or DataAdapter.
As mentioned in comments use % for wildcard outside of Access.
Public Function GetRow() As DataRow
Dim SQL = "SELECT * FROM Players WHERE [Name] LIKE 'B%' ORDER BY [Name]"
Dim DT As New DataTable With {.Locale = Globalization.CultureInfo.InvariantCulture}
Try
Using con As New OleDbConnection("Your connection string"),
Cmd As New OleDbCommand(SQL, con)
con.Open()
DT.Load(Cmd.ExecuteReader)
End Using 'Closes and disposes the connection and command
Catch Ex As OleDbException
MsgBox(Ex.Message)
End Try
If DT.Rows.Count = 0 Then
Return Nothing
Else
Return DT.Rows(0)
End If
End Function

Error on DataGridView reading a certain Field

I'm creating a program for a company I'm currently in internship, one part of it is being able to see the data from their SQL 2014 server.
I used a DatagridViewe to get the data from the SQL server and at first it works
But if I scroll sideways it shows me this error
I tried searching but I couldn't find anyone with the same problem
Private Sub dothesearch()
Dim comandstring As String
If combo_basededados.SelectedIndex = 0 Then
comandstring = "SELECT * FROM PRITESTES.dbo.RegrasDescPrec"
Else
comandstring = "SELECT * FROM PRITESTES.dbo.EscaloesRegrasDescPrec"
End If
Try
conn = New SqlConnection(conectionsString)
conn.Open()
Dim comand As New SqlCommand(comandstring, conn)
Dim reader As SqlDataReader = comand.ExecuteReader
Dim datatableusing As New DataTable
datatableusing.Load(reader)
DTGV.DataSource = datatableusing
Catch ex As IndexOutOfRangeException
MsgBox(ex.Message)
Finally
conn.Close()
End Try
End Sub

Visual Basic, Copying Success but does not Insert data into SQL TABLE

I have some problems here. I need help.
Recently, I have created a local database called stock.mdf and the application will be getting all the data from the hosting MySQL database into this local SQL Server database.
I am using sqlBulkCopy to inserting all the data. I have tried to view it after inserting. But when I close my application, I head back to check the table data. It does not inserted. Why is that?
Here is my code:
Here will be retrieving the data from the hosting
Dim connStr As String = "server=xxxx;user=xxx;database=xxx;password=xxxx;"
Dim conn As New MySqlConnection(connStr)
Dim cmd As New MySqlCommand
Dim Adapter As New MySqlDataAdapter
Dim StockData As New DataTable
Try
Dim SQL As String = "SELECT * FROM stock"
Console.WriteLine("Connecting to MYSQL.....")
conn.Open()
cmd.Connection = conn
cmd.CommandText = SQL
Adapter.SelectCommand = cmd
Adapter.Fill(StockData)
' StockViewGrid.DataSource = StockData
Catch ex As Exception
Console.WriteLine(ex.ToString())
Finally
conn.Close()
Console.Write("Done")
End Try
This will be the places where sqlBulkCopy working:
As well, I am trying to view from the stock table.
Dim Local_connectionStr As String = "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|stock.mdf;Integrated Security=True"
Dim Local_conn As New SqlConnection(Local_connectionStr)
Dim Local_cmd As New SqlCommand
Dim Local_Adapter As New SqlDataAdapter
Dim Local_StockData As New DataTable
Try
Using sqlBulkCopy As New SqlBulkCopy(Local_conn)
'Set the database table name
sqlBulkCopy.DestinationTableName = "stock"
'[OPTIONAL]: Map the DataTable columns with that of the database table
sqlBulkCopy.ColumnMappings.Add("stockId", "stockId")
sqlBulkCopy.ColumnMappings.Add("id_android", "id_android")
sqlBulkCopy.ColumnMappings.Add("itemCode", "itemCode")
sqlBulkCopy.ColumnMappings.Add("quantity", "quantity")
Local_conn.Open()
sqlBulkCopy.WriteToServer(StockData)
Local_conn.Close()
End Using
Catch ex As Exception
Console.WriteLine(ex.ToString())
Finally
Local_conn.Close()
Console.Write("Done")
End Try
Try
Dim SQL As String = "SELECT * FROM stock"
Console.WriteLine("Connecting to MYSQL.....")
Local_conn.Open()
Local_cmd.Connection = Local_conn
Local_cmd.CommandText = SQL
Local_Adapter.SelectCommand = Local_cmd
Local_Adapter.Fill(Local_StockData)
StockViewGrid.DataSource = Local_StockData
Catch ex As Exception
Console.WriteLine(ex.ToString())
Finally
Local_conn.Close()
Console.Write("Done")
End Try
Since I can't comment (not enough points) I will put my thoughts here. Can you check that you got the expected data with a
Console.WriteLine(StockData.Rows.Count.ToString)
Console.ReadLine()
Also, your destination table doesn't have a PK that is auto increment, does it?
Sorry about the comment as an answer. (Untested code)
Unfortunately your code extracts are lacking headers etc, so are not complete, and I cannot be absolutely certain, BUT in your retrieving routine you seem to be declaring StockData as a local variable. This means that, although this may well be being filled with the data from MySQL, it is immediately discarded on exiting from this routine. Therefore the StockData variable in the sqlBulkCopy routine will be empty, and therefore will insert nothing. You need to fix your scope and make sure that the StockData read from MySQL is the same StockData used in SqlBulkCopy.

How do I create a dataset in VB.NET from this code?

I currently have the following code in my project which populates a DataGridView object with the results of an SQL query.
Sub PerformQuery(ByVal SQLText As String)
Dim DbConnection As New OleDb.OleDbConnection(createConnectionString)
Dim SQLQuery As String = SQLText
Dim Adapter As New OleDb.OleDbDataAdapter(SQLQuery, DbConnection)
Try
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Elsewhere in my project I can create a DataSet object using the code
Dim ds As New DataSet
And then extract data from it using code like:
MaxRows = ds.Tables("Dataset_Users").Rows.Count
Rather than populating a DataGridView, how can I use the PerformQuery code to create a dataset?
Thank you in advance for your help.
I think you are after the following:
Try
Dim ds As New DataSet
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
ds.Table.Add(Table)
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Or as in your example you was after the Number of rows in the dataset you can do the same with the DataTable, For Example:
Try
Dim MaxRows As Integer
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
'' Getting the number of rows in the DataTable
MaxRows = Table.Rows.Count
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Think in a more functional style. Return the table instead of setting to grid. While we're here, let's update the method so you don't have to write queries anymore that leave you wide open to sql injection attacks:
Function PerformQuery(ByVal SQLText As String, ByVal ParamArray Parameters() As OleDbParameter) As DataTable
Dim result As New DataTable()
Using cn As New OleDb.OleDbConnection(createConnectionString), _
cmd As New OleDb.OleDbCommand(SQLText, cn), _
Adapter As New OleDb.OleDbDataAdapter(cmd, cn)
If Parameters IsNot Nothing AndAlso Parameters.Length > 0 Then
cmd.Parameters.AddRange(Parameters)
End If
Adapter.Fill(result)
End Using
Return Result
End Function
Note that I also removed the error handling and locale code. You still need to do that stuff, but when you want to just return a datatable instead of interact directly with the user interface in a method you have effectively moved your code to a lower level of abstraction. When you do that, you probably don't want to deal with the error handling at this lower level any more; instead let the exceptions bubble up where you can handle them closer to the user interface.
Now you call the updated method like this:
Dim sql As String = "SELECT * FROM Customers WHERE CustomerID = ?"
Dim CustID As New OleDb.OleDbParameter("CustomerId", OleDbType.Integer)
CustID.Value = 123456
Try
DataGridView1.DataSource = PerformQuery(sql, CustID)
Catch Ex As Excpetion
MsgBox(Ex.Message)
End Try

SQLXML Import/Export

I have a SQL DB from which I export data as XML using VB.Net code. The code is relatively simple, works quickly, and formats the XML beautifully. The code is:
Dim connetionString As String
Dim connection As SqlConnection
Dim adapter As SqlDataAdapter
Dim ds As New DataSet
Dim sql As String
connetionString = "**connectionstring**"
connection = New SqlConnection(connetionString)
sql = "select * from costdata"
Try
connection.Open()
adapter = New SqlDataAdapter(sql, connection)
adapter.Fill(ds)
connection.Close()
ds.WriteXml("**PATH**")
MsgBox("Done")
Catch ex As Exception
MsgBox(ex.ToString)
End Try
The problem I'm having is loading this data back in. It seems like it should be as easy as the above, but I can't seem to get a simple way to do it.
It's my understanding that I can use an XMLReader coupled with ADO.NET, but in that case I need to define the columns for the DataTable to insert the XML Data into before I import it all into the DB.
Is there any way to keep from having to hard-code column values in the DataTable, and have the exported XML data import in similar fashion to the above?
Though it's not automated by column name, I decided that hardcoding the mappings wasn't too big a hassle. I'm all ears for an automated way, however. My solution:
Dim connectionString As String = "Data Source=(localdb)\v11.0;Initial Catalog=localACETest;Integrated Security=True"
Try
Using sqlconn As New SqlConnection(connectionString)
Dim ds As New DataSet()
Dim sourcedata As New DataTable()
ds.ReadXml("C:\Users\coopere.COOPERE-PC\Desktop\Test.xml")
sourcedata = ds.Tables(0)
sqlconn.Open()
Using bulkcopy As New SqlBulkCopy(sqlconn)
bulkcopy.DestinationTableName = "ScheduleData"
bulkcopy.ColumnMappings.Add("Id", "Id")
bulkcopy.ColumnMappings.Add("Period", "Period")
...
bulkcopy.WriteToServer(sourcedata)
End Using
sqlconn.Close()
End Using
MsgBox("Done")
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Here is a way to automate column mappings ... it assumes the table exists with the same structure in the target database. Cheers :-)
Public Shared Function BulkCopyXML( _
path_ As String, _
connection_string_ As String, _
messages_ As List(Of String), _
exceptions_ As List(Of Exception) _
) As Boolean
Dim result_ As Boolean = False
Try
Dim dataset_ As New DataSet()
dataset_.ReadXml(path_)
Dim datatable_ As DataTable = Nothing
Using connection_ As SqlClient.SqlConnection = New SqlClient.SqlConnection(connection_string_)
connection_.Open()
Using bulkCopy_ As SqlClient.SqlBulkCopy = New SqlClient.SqlBulkCopy(connection_)
For Each datatable_ In dataset_.Tables()
messages_.Add(datatable_.TableName)
bulkCopy_.DestinationTableName = datatable_.TableName
bulkCopy_.ColumnMappings.Clear()
For Each dataColumn_ As DataColumn In datatable_.Columns
bulkCopy_.ColumnMappings.Add(dataColumn_.ColumnName, dataColumn_.ColumnName)
Next
bulkCopy_.WriteToServer(datatable_)
Next
End Using
End Using
result_ = True
Catch exception_ As Exception
If exceptions_ Is Nothing Then
Throw exception_
Else
exceptions_.Add(exception_)
End If
Finally
End Try
Return result_
End Function