VB.Net MySQL Connector. Bulk insert stopped working - mysql

I've been using the code below to populate tables in a MySQL database for a few years and it's been working fine.
Imports MySql.Data.MySqlClient
Public sub Thing(dt as datatable)
Try
'dt is datatable object populated from MSsqlServer
Dim MySQLcmdSelect As MySqlCommand = New MySqlCommand("Select * From " & tableName) With {
.Connection = MySQLcnn
}
Dim MySQLad As New MySqlDataAdapter(MySQLcmdSelect)
Dim MySQLcmdBuilder As New MySqlCommandBuilder(MySQLad)
Dim MySQLcmd As MySqlCommand = MySQLcmdBuilder.GetInsertCommand()
MySQLcmd.Connection = MySQLcnn
MySQLad.InsertCommand = MySQLcmd
MySQLad.Update(dt)
Catch ex As Exception
Debug.Print(ex.Message)
Console.WriteLine("Error when populating " + tableName + ": " + ex.Message + vbCrLf + ex.InnerException.ToString)
WriteLog(ex.Message)
End Try
End Sub
The problem is, it's stopped working!
The connector seems to be working ok as I can drop tables and create them, but this will not let me bulk insert the contents of the datatable 'dt' into the MySQL table.
It doesn't throw an exception, it just passes over the .update(dt) line as if it were a Debug.Print() line when there's 1000s of rows in dt.
Effectively I'm selectively extracting data from tables in MS SQLserver and bulk uploading them to tables with the same name in MySQL.
a) is this the best way to do it, and
b) why has this suddenly stopped working?
According to phpMyAdmin its Server version: 5.6.51 - MySQL Community Server (GPL)
EDIT: Further info... all the SQL commands to drop existing tables and create tables work. It is only the .update(dt) that doesn't work.

Possible problems with your code could be that the schemas do not match exactly, auto increment columns, or the connection could have been disposed elsewhere (it should be declared local to the method where it is used). Another problem is that dt has had AcceptChanges called or LoadOptions were set incorrectly.
Private ConnStr As String = "Your connection string"
Public Sub Thing(dt As DataTable, tableName As String)
'check the datatable - if it is ok then this check is probably not necessary
Dim changesDT = dt.GetChanges()
If changesDT.Rows.Count < 1 Then
MessageBox.Show("No changes to update")
Exit Sub
End If
Using cn As New MySqlConnection(ConnStr),
MySQLad As New MySqlDataAdapter("Select * From " & tableName, cn)
cn.open() ' added by OP
Dim MySQLcmdBuilder As New MySqlCommandBuilder(MySQLad)
MySQLad.Update(dt)
End Using
End Sub

Perhaps you have "0000-00-00"? If so, a setting has changed the default -- new versions of MySQL disallow zero dates. Read about sql_modes.

Still can't get the bulk upload to work, but we have redesigned this so that data is inserted into MySQL at the same time we store it in the MS-SQL database and have removed the need to bulk upload.

Related

How to execute two separate queries from one DBCommand object?

I have the following code that tries to get records from two different tables and then add them to the particular comboboxes. Only the first query works and the second one is ignored.
Try
sqlConn = New MySqlConnection
connStr = New String("Server = localhost; Database = gen_database; Uid = root; Pwd =")
sqlConn.ConnectionString = connStr
myCommand = New MySqlCommand("Select DevCompanyName from developer_name_table; Select DevType from development_type_table")
myCommand.CommandType = CommandType.Text
myCommand.Connection = sqlConn
ComboBox1.Items.Clear()
sqlConn.Open()
MsgBox("Connection Open.")
dR = myCommand.ExecuteReader()
Do While dR.Read()
ComboBox1.Items.Add(dR("DevCompanyName"))
ComboBox2.Items.Add(dR("DevType")) 'Error shows here Could not find specified column in results: DevType
Loop
Catch ex As MySqlException
MsgBox(ex.ToString)
Finally
dR.Close()
sqlConn.Close()
End Try
I can think of another way which is to do it in multiple query but can the code be simplified to something like this?
Using a DBDataReader with 2 queries, only only the first executes because there is no way to signal which table/query each read item is from. Your "double read" loop seems to assume they will be returned at the same time (rather than one query after the other - the same way they are sent to the DBCOmmand); if it did work that way, it would fail whenever there are not the same number of rows in each table.
Using DataTables affords you the chance to simply bind the result to your combos rather than copying data into them:
Dim SQL = "SELECT * FROM Sample; SELECT * FROM Simple"
Dim ds As New DataSet
Using dbcon As New MySqlConnection(MySQLConnStr),
cmd As New MySqlCommand(SQL, dbcon)
dbcon.Open()
Dim da As New MySqlDataAdapter(cmd)
da.Fill(ds)
End Using
' debug results
Console.WriteLine(ds.Tables.Count)
Console.WriteLine(ds.Tables(0).Rows.Count)
Console.WriteLine(ds.Tables(1).Rows.Count)
If I look at the output window, it will print 2 (tables), 10000 (rows in T(0)) and 6 (rows in T(1)). Not all DBProviders have this capability. Access for instance will choke on the SQL string. Other changes to the way your code is composed:
DBConnections and DBCommand objects need to be disposed. They allocate resources, so they need to be created, used and disposed to release those resources.
The Using block does that for us: The target objects are created at the start and closed and disposed at End Using.
The code above stacks or combines 2 such blocks.
The code fills a DataSet from the query, in this case creating 2 tables. Rather than copying the data from one container to another (like a control), you can use a DataTable as the DataSource:
cboDevName.DataSource = ds.Tables(0)
cboDevName.DisplayMember = "DevName" ' column names
cboDevName.ValueMember = "Id"
cboDevType.DataSource = ds.Tables(1)
cboDevType.DisplayMember = "DevType"
cboDevType.ValueMember = "DevCode"
The result will be all the rows from each table appearing in the respective combo control. Typically with this type of thing, you would want the ID/PK and the name which is meaningful to the user in the query. The user sees the friendly name (DisplayMember), which the code can easily access the unique identifier for the selection (ValueMember).
When using bound list controls, rather than using SelectedIndex and the SelectedIndexChanged event, you'd use SelectedValue and SelectedItem to access the actual data.
MSDN: Using Statement (Visual Basic)
You can use .net connector (download here)
Then you can install it and add it to your project (project -> references -> add -> browse).
Finally add import:
Imports MySql.Data.MySqlClient
So you'll be able to use this:
Dim connStr as String = "Server = localhost; Database = gen_database; Uid = root; Pwd ="
Dim SqlStr as String = "Select DevCompanyName from developer_name_table; Select DevType from development_type_table"
Dim ds As DataSet = MySqlHelper.ExecuteDataset(CnStr, SqlStr)
ds will contain two datatables: one for each query

Update local database all modified data to server database

I'm facing a problem when I want to update data from local database to server data, replacing everything that has been modified at local database. I know it might be simple but I got no idea about this, so any help will be appreciate.
In my situation, I want to use a button to upload all modified data to
the server database. Now I'm just using 2 databases at same server to do
testing.
Private Sub btnUp_Click(sender As System.Object, e As System.EventArgs) Handles btnUp.Click
localconn.ConnectionString = lctext
serverconn.ConnectionString = sctext
Try
localconn.Open()
serverconn.Open()
Dim localcmd As New OdbcCommand("select a.acc_id as localid, a.acc_brcid, a.smartcardid, a.acc_created, a.acc_modified as localmodified, b.acd_firstname, b.acd_ic, b.acd_oldic, b.acd_race, b.acd_dob, b.acd_rescity, b.acd_resaddr1, b.acd_telmobile, b.acd_email, b.acd_telwork, b.acd_modified, b.acd_accid from nsk_account a inner join nsk_accountdetail b on a.acc_id = b.acd_accid", localconn)
Dim servercmd As New OdbcCommand("select c.acc_id, c.acc_brcid, a.smartcardid, c.acc_created, c.acc_modified, d.acd_firstname, d.acd_ic, d.acd_oldic, d.acd_race, d.acd_dob, d.acd_rescity, d.acd_resaddr1, d.acd_telmobile, d.acd_email, d.acd_telwork, d.acd_modified, d.acd_accid from nsk_account c inner join nsk_accountdetail d on c.acc_id = d.acd_accid", serverconn)
localcmd.CommandType = CommandType.Text
Dim rdr As OdbcDataReader = localcmd.ExecuteReader()
Dim thedatatable As DataTable = rdr.GetSchemaTable()
'localcmd.Parameters.Add("#localid", OdbcType.Int, "a.acc_id")
'localcmd.Parameters.Add("#localmodified", OdbcType.DateTime, "b.acd_modified")
Dim localid As String
Dim localmodi As String
localcmd.Parameters.AddWithValue("localid", localid)
localcmd.Parameters.AddWithValue("localmodified", localmodi)
For Each localid In thedatatable.Rows
Dim calldata As New OdbcCommand("SELECT acc_modified from nsk_account where acc_id ='" + localid + "'", serverconn)
Dim reader As OdbcDataReader = calldata.ExecuteReader
txtSDate.Text = reader("acc_modified").ToString
If localmodi <= txtSDate.Text Then
'do nothing, proceed to next data
Else
Dim ACCoverwrite As New OdbcCommand("Update nsk_account SET smartcardid = #mykad, acc_created = #created, acc_modified = #modify WHERE acc_id ='" + localid + "'", serverconn)
Dim DEToverwrite As New OdbcCommand("Update nsk_accountdetail SET acd_firstname = #name, acd_ic = #newic, acd_oldic = #oldic, acd_race = #race, acd_dob = #dob, acd_rescity = #city, acd_resaddr1 = #address, acd_telmobile = #phone, acd_email = #email, acd_telwork = #language, acd_modified = #detmodify WHERE acd_accid ='" + localid + "'", serverconn)
ACCoverwrite.ExecuteNonQuery()
DEToverwrite.ExecuteNonQuery()
End If
Next
MessageBox.Show("Upload success", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Finally
localconn.Close()
serverconn.Close()
End Try
End Sub
any comment or suggestion will be appreciate.
I hope you mean table by table. I didn't read your code much but you got the idea - you need 2 connections but here where there are 2 distinct ways of doing it.
Way #1 - you can use when amounts of data (how to say it better? - not huge). You can load a DataTable object with data from server and update changed records. You can use DataAdapter and issue CommitChanges - all changed/new rows will be written to server.
NOTE: you need a mechanism that will reliably able to tell which rows are new and modified on your local DB. Are you OK if your PK in local DB will be different than on the server? You need to answer these questions. May be you need a special mechanism for PK locally. For example, add rows using negative PK integers, which will tell you that these rows are new. And use "ModifiedDate", which together with PK will tell if the row needs updating.
Way #2 - use anytime, even with larger amount of data. Take a local row and examine it. If it is new - insert, if it is existing and "DateModified" changed - do update. There are variations of how to do it. You can use SQL MERGE statement, etc.
But these are two major ways - direct row insert/update and disconnected update/mass commit.
Also, you can do it in bulk, using a transaction - update some rows-commit, and start new transaction. This will help if the application being used as you updating it.
I hope these ideas help. If you do what you do, where you have
For Each localid In thedatatable.Rows
I am not sure what localid is. It should be
' prepare command before loop
sql = "Select * From Table where ID = #1"
' you will create parameter for #1 with value coming from
' row("ID")
Dim cmd As New .....
cmd.Parameters.Add(. . . . )
For Each row As DataRow In thedatatable.Rows
cmd.Parameters(0).Value = row("ID") ' prepare command upfront and only change the value
using reader as IDataReader = cmd.ExecuteReader(. . . . )
If Not reader.Read() Then
' This row is not found in DB - do appropriate action
Continue For
Else
' here check if the date matches and issue update
' Better yet - fill some object
End if
end using
' if you fill object with data from your row -here you can verify if
' update needed and issue it
. . . . . .
Next

MySqlDataReader not returning data

As part of a project to import data into wordpress via screen scraping I've a database table of old and new URL's stored in a MySQL database. In the example below the ExecuteReader command doesn't appear to be returning any data (-1 rows effected), I've ran the SQL via workbench and that returns data, so it's not the SQL or data in the database.
At other times within the code I've called ExecuteNonQuery() and ExecuteScalar() both without issue (so it isn't the connection string).
Any ideas what to try next?
Dim SQL As String
Dim conn As MySqlConnection = New MySqlConnection(_CONNECTIONSTRING)
SQL = "SELECT OrgURL, NewURL FROM `wp_user`.`tbl_linkdata`"
Try
conn.Open()
Dim cmd As MySqlCommand = New MySqlCommand(SQL, conn)
Dim dr As MySqlDataReader = cmd.ExecuteReader()
While (dr.Read)
LinkHashMap.Add(dr.GetString(0), dr.GetString(1))
End While
Console.ForegroundColor = ConsoleColor.Cyan
Console.WriteLine("The Hash map contains " + dr.RecordsAffected + " rows")
dr.Close()
Catch ex As Exception
Console.ForegroundColor = ConsoleColor.Red
Console.WriteLine("Exception loading the hashtable : " + ex.Message)
Finally
conn.Dispose()
End Try
DataReader.RecordsAffected always returns -1 for a SELECT command. What does LinkHashMap.Count return? In MySqlDataReader it is the same:
"The number of rows changed, inserted, or deleted. -1 for SELECT
statements"
If you want to count the number of records you can use LinkHashMap.Count.
You: "LinkHashMap is "Nothing" "
How do you want to add something to it without initializing it first? A NullReferenceException should have happened. So initialize the dictionary (or whatever it is) first via constructor:
Dim LinkHashMap As New Dictionary(Of String, String)
While (dr.Read)
LinkHashMap.Add(dr.GetString(0), dr.GetString(1))
End While

Why is my if statement not being evaluated correctly?

Good evening,
I'm using the following code to truncate a table in my MySQL database. From what I can tell, the query is running fine and the tables are being truncated. However, the if statement that I'm using to test if rows are affected is being evaluated on the Else statement.
So how come the table in the database is being truncated - as expected - but the Else statement is being evaluated - as if no rows are affected? What am I doing wrong?
HERE'S THE CODE:
Public Sub purgeCC()
Dim dbAdapter As New MySqlDataAdapter
Dim dbCmd As New MySqlCommand
Dim ConnectionString As String = String.Format("Server={0};Port={1};Uid={2};Password={3};Database=accounting", FormLogin.ComboBoxServerIP.SelectedItem, My.Settings.DB_Port, My.Settings.DB_UserID, My.Settings.DB_Password)
Dim myQuery As String = "TRUNCATE TABLE cc_master"
Using dbConn As New MySqlConnection(ConnectionString)
Using dbComm As New MySqlCommand()
With dbComm
.Connection = dbConn
.CommandType = CommandType.Text
.CommandText = myQuery
End With
Try
Dim affectedRow As Integer
dbConn.Open()
affectedRow = dbComm.ExecuteNonQuery()
If affectedRow > 0 Then
MsgBox("Credit Card Master table has been successfully purged!", MsgBoxStyle.Information, "DATABASE PURGED!")
Else
MsgBox("Credit Card Master table was not purged!", MsgBoxStyle.Critical, "ATTENTION")
End If
Catch ex As Exception
MessageBox.Show("A DATABASE ERROR HAS OCCURED" & vbCrLf & vbCrLf & ex.Message & vbCrLf & _
vbCrLf + "Please report this to the IT/Systems Helpdesk at Ext 131.")
End Try
dbConn.Close()
dbComm.Dispose()
End Using
End Using
End Sub
Per this document http://dev.mysql.com/doc/refman/5.0/en/truncate-table.html
the truncate command may not always return the rows affected if the DB version is greater than 5.somthing and the table does not have foreign key constraints. if you do have FKs then a delete is processed for each row and you get the return value you are seeking, but if you don't then mysql will drop and re-add the table (which is much faster), which means it has no ideas how many records were affected.
Your if statement is being evaluated correctly, it's just that affectedRow (which should probably be affectedRows by the way, but I'm just being pedantic there) is not being set as you expect.
It's not wise to depend on the affected rows count for a truncation as per the online docs for truncate table.
In some cases, it's only accurate if it gets mapped to a delete (such as specific versions with the InnoDB engine where there are foreign key constraints, since they have to be checked or cascaded).
With other engines, you may get 0 because it drops the table and then recreates it.
If you really want the information, execute a select count(*) before and after the truncation. The after should usually be zero, of course (unless the truncation fails).

How to use data reader in vb.net

Please help, how do I really use data reader in vb.net. I'm using odbc to connect mysql and vb.net.
Function I declared on a module:
Public Function form2search(ByVal drugname As String) As OdbcDataReader
cmd.CommandText = "SELECT * FROM drug WHERE Drug_name LIKE'%" & drugname & "' "
Return cmd.ExecuteReader
End Function
text_changed event:
con.drugname=textBoxdrugname.text
Dim rdr As Odbc.OdbcDataReader
rdr = con.form2search(drugname)
if rdr.hasrows=true then
rdr.read()
TextBoxdrugname.Text = rdr("Drug_name").ToString
TextBoxdrugcode.Text = rdr("Drug_code").ToString
drugtype.Text = rdr("Drug_type").ToString
end if
I see a result, but it only loads the first item on the database. I've put this code in the text_changed event. What's the proper way of doing this? And what's wrong with the 2nd code, why is it only loading the first data
As you can see the con is the module where I declared the function. Then I created an object of it in the form.
The DataReader is a low level implementation that does not support navigation and only reads a single row every time you call
reader.Read()
For a Windows Forms app you probably should use a DataSet / DataTable approach or a ORM. And you should consider using the mysql connector net over the odbc driver. It is available at mysql.com.
Here is a little demo code:
dim table as new DataTable("table1")
' Create a Connection
using conn as new MysqlConnection("...connectionstring")
conn.Open() ' Open it
' Create a new Command Object
using cmd as new MysqlCommand("SELECT * FROM table", conn)
' Create a DataAdapter
' A DataAdapter can fill a DataSet or DataTable
' and if you use it with a CommandBuilder it also
' can persist the changes back to the DB with da.Update(...)
using da as new MysqlDataAdapter(cmd)
da.Fill(table) ' Fill the table
end using
end using
end using
' A Binding Source allows record navigation
dim bs as new BindingSource(table, nothing)
' You can bind virtually every property (most common are "text" "checked" or "visible"
' of a windows.forms control to a DataSource
' like a DataTable or even plain objects
textBox1.DataBindings.Add("Text", bs, "columnName")
' Now you can navigate your data
bs.MoveNext()
' Even a ComboBox can be bound to a List and display the related value
' of your current row
comboBox1.DataSource = table2
comboBox1.DisplayMember = "name"
comboBox1.ValueMember = "id"
comboBox1.DataBindings.Add("SelectedValue", bs, "id")
You have to just put the object of data reader in while loop.Here is the sample code:
dr = myCommand.ExecuteReader()
While dr.Read()
'reading from the datareader
MessageBox.Show("colname1" & dr(0).ToString())
MessageBox.Show("colname2" & dr(1).ToString())
MessageBox.Show("colname3" & dr(2).ToString())
MessageBox.Show("colname4" & dr(3).ToString())
MessageBox.Show("colname5" & dr(4).ToString())
'displaying the data from the table
End While
dr.Close()