Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / database / MySQL

MySQL Data Importer

3.86/5 (5 votes)
20 Feb 2019CPOL3 min read 17.6K   602  
Tool that will let you import data from SQL Server and Access to MySQL

Introduction

I needed to import data from MS SQL Server to MySQL. I first tried to use MySQL Workbench but ran into some problem. So I wrote a tool to copy table schema and data to MySQL. You can download it here.

Try something like:

SQL
"<div style='background-image:url(bg.aspx?color=99ccff&value="+ cstr(

CASE [Organisation].[Level 03].CurrentMember.Name
WHEN 'Main' THEN [Measures].[Total monthly profit]/1500*100
ELSE             [Measures].[Total monthly profit]/500*100
END

)+"); background-repeat:no-repeat; width: 100px; text-align:right'>"+
format([Measures].[Unit Sales],"Standard")+"</div>"

Image 1

Background

I have tested the tool against MS SQL Server and Access databases. Not all data types are supported. You can modify the application to support other data types and databases (like Oracle).

Required Components

In case you do not have MySQL ODBC driver or Microsoft Access OLEDB provider, you can download them here:

  1. Microsoft Access Database Engine
  2. MySQL ODBC driver - select version and choose "No thanks, just start my download."

Using the Code

When you click the connect button, you will get OLEDB dialog asking you to select the provider and the connection information. Here, you can also test the connection. The connection string returned by EditConnectionString() function is stored in the Windows registry.

Image 2

VB.NET
Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles btnConnect.Click

    Dim sConnectionString As String = "Provider=SQLOLEDB.1"
    sConnectionString = EditConnectionString(sConnectionString)
    If sConnectionString = "" Then
        Exit Sub
    End If

    txtConnect.Text = sConnectionString

    Dim oAppRegistry As New AppRegistry
    oAppRegistry.SetRegistryValue("ConnectionString", sConnectionString)

    SetTableCombo()
End Sub

EditConnectionString function uses DataLinks ActiveX object to generate the connection string based on user input. The ease of working with COM objects is the primary reason I chose VB.NET for this app.

VB.NET
Protected Function EditConnectionString(ByVal sConnectionString As String) As String
    Try
        Dim oDataLinks As Object = CreateObject("DataLinks")
        Dim cn As Object = CreateObject("ADODB.Connection")

        cn.ConnectionString = sConnectionString
        oDataLinks.hWnd = Me.Handle

        If Not oDataLinks.PromptEdit(cn) Then
            'User pressed cancel button
            Return ""
        End If

        cn.Open()

        Return cn.ConnectionString

    Catch ex As Exception
        MsgBox(ex.Message)
        Return ""
    End Try
End Function

Once you enter MySQL server information and click Test, the connection information will be saved into registry and GetMySqlConnectionString() will create the MySQL connection string. The user will see Success message box or an Error message box.

VB.NET
Private Sub txtTest_Click(sender As System.Object, e As System.EventArgs) Handles txtTest.Click

    Dim oAppRegistry As New AppRegistry
    oAppRegistry.SetRegistryValue("Driver", cboDriver.Text)
    oAppRegistry.SetRegistryValue("Server", txtServer.Text)
    oAppRegistry.SetRegistryValue("Database", txtDatabase.Text)
    oAppRegistry.SetRegistryValue("User", txtUser.Text)
    oAppRegistry.SetRegistryValue("Password", txtPassword.Text)

    Try
        Dim cnOdbc As OdbcConnection = New OdbcConnection(GetMySqlConnectionString())
        cnOdbc.Open()
        cnOdbc.Close()
    Catch ex As Exception
        MsgBox(ex.Message)
        Exit Sub
    End Try

    MsgBox("Success")
End Sub

When the form is opened, this sub will retrieve the connection string from the registry. It will also call SetTableCombo() to populate the list of available tables.

VB.NET
Private Sub frmExport_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    Dim oAppRegistry As New AppRegistry
    txtConnect.Text = oAppRegistry.GetRegistryValue("ConnectionString")

    If txtConnect.Text <> "" Then
        SetTableCombo()
    End If

    Dim sDriver As String = oAppRegistry.GetRegistryValue("Driver")
    If sDriver = "" Then
        cboDriver.SelectedIndex = 0
    Else
        cboDriver.SelectedItem = sDriver
    End If

    txtServer.Text = oAppRegistry.GetRegistryValue("Server")
    txtDatabase.Text = oAppRegistry.GetRegistryValue("Database")
    txtUser.Text = oAppRegistry.GetRegistryValue("User")
    txtPassword.Text = oAppRegistry.GetRegistryValue("Password")
End Sub

SetTableCombo() to populate the list of available tables. It uses GetOleDbSchemaTable() function to get list of tables from an OLEDB connection.

VB.NET
Sub SetTableCombo()
    cboTable.Items.Clear()

    Dim oTable As DataTable
    Try
        Dim cnOleDb As New OleDbConnection(txtConnect.Text)
        cnOleDb.Open()

        oTable = cnOleDb.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, _
         New Object() {Nothing, Nothing, Nothing, "TABLE"})
        cnOleDb.Close()
    Catch ex As Exception
        MsgBox(Err.Description)
        Exit Sub
    End Try

    For i As Integer = 0 To oTable.Rows.Count - 1
        Dim sSchema As String = oTable.Rows(i)("TABLE_SCHEMA") & ""
        Dim sTableName As String = oTable.Rows(i)("TABLE_NAME") & ""
        cboTable.Items.Add(sTableName)
    Next

    If cboTable.Items.Count > 0 Then
        cboTable.SelectedIndex = 0
    End If

End Sub

When you click on the "Copy tables" button, this sub will check if SetTableCombo() populated the table list and will also check if user selected a table(s) to export. Next, it will loop through the list of selected tables and call ExportTable sub for each table.

VB.NET
Private Sub btnExport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles btnExport.Click

    If cboTable.Items.Count = 0 Then
        MsgBox("Please connect to the source database.")
        Exit Sub
    End If

    If cboTable.CheckedItems.Count = 0 Then
        MsgBox("Please select tables to copy.")
        Exit Sub
    End If

    txtLog.Clear()

    If cboTable.CheckedItems.Count > 0 Then
        Dim cnOdbc As OdbcConnection = New OdbcConnection(GetMySqlConnectionString())
        cnOdbc.Open()

        Dim cn As New OleDbConnection(txtConnect.Text)
        cn.Open()

        For i As Integer = 0 To cboTable.CheckedItems.Count - 1
            Dim sTable As String = cboTable.CheckedItems(i).ToString
            ExportTable(sTable, cn, cnOdbc)
        Next

        cnOdbc.Close()
        cn.Close()
    End If

End Sub

ExportTable is the most important sub:

  1. It will first get the record count for the destination table into iMySqlRecCount.
  2. If there is data and the user had selected "Empty target table before insert", all records in the destination table will be deleted.
  3. If user had selected "Create target table (drop if exists), the sub will drop destination table (if it exists) and will create new destination table. GetColumnsTableAccess function will be used to get the SQL for Access and GetColumnsTableSqlServer will be used for SQL Server.
  4. It will first get the record count for the source table into iCount.
  5. Finally, it will generate the INSERT INTO statement for each source record and run it against the destination database.
VB.NET
Private Sub ExportTable(ByVal sTableName As String, ByRef cn As OleDbConnection, _
ByRef cnOdbc As OdbcConnection)

    Dim bMySqlTableExists As Boolean = False
    Dim iMySqlRecCount As Integer = 0

    Try
        Dim oMySqlCmd As New OdbcCommand("SELECT Count(*) FROM " & sTableName, cnOdbc)
        iMySqlRecCount = Integer.Parse(oMySqlCmd.ExecuteScalar().ToString())
        bMySqlTableExists = True
    Catch ex As Exception
        'Ignore - assume table dos not exist
    End Try

    If chkDeleteData.Checked And iMySqlRecCount > 0 Then
        Log("Deleting data from table: " & sTableName)

        OpenConnections(cn, cnOdbc)
        Dim sSql1 As String = "DELETE FROM " & sTableName
        Dim oCmd1 As New OdbcCommand(sSql1, cnOdbc)

        Try
            oCmd1.ExecuteNonQuery()
        Catch ex As Exception
            Log(ex.Message & vbTab & "SQL: " & sSql1)
        End Try
    End If

    If chkCreateTable.Checked Then

        If bMySqlTableExists Then
            Log("Drop table: " & sTableName)

            Dim oCmdDrop As New OdbcCommand("DROP TABLE " & PadQuotes(sTableName), cnOdbc)

            Try
                oCmdDrop.ExecuteNonQuery()
                bMySqlTableExists = False
            Catch ex As Exception
                Log("Could not drop table: " & sTableName & ", " & ex.Message & vbTab)
            End Try

        End If

        Log("Create table: " & sTableName)
        OpenConnections(cn, cnOdbc)

        'Make create table statement
        Dim sConnectionString As String = txtConnect.Text.ToUpper()
        Dim sSql1 As String = ""

        If sConnectionString.IndexOf(".ACE.OLEDB") = -1 _
    OrElse sConnectionString.IndexOf(".JET.OLEDB") = -1 Then
            sSql1 = GetColumnsTableAccess(sTableName, cn)
        Else
            sSql1 = GetColumnsTableSqlServer(sTableName, cn)
        End If

        Dim oCmd1 As New OdbcCommand(sSql1, cnOdbc)

        Try
            oCmd1.ExecuteNonQuery()
            bMySqlTableExists = True
        Catch ex As Exception
            Log(ex.Message & vbTab & "SQL: " & sSql1)
        End Try
    End If

    Dim sSql As String = "SELECT Count(*) FROM " & PadQuotes(sTableName)
    Dim cmd As New OleDbCommand(sSql, cn)

    Dim iCount As Integer = Integer.Parse(cmd.ExecuteScalar().ToString())
    If iCount = 0 Then
        'Nothing to copy - Exit
        Exit Sub
    End If

    If bMySqlTableExists = False Then
        Log("Destination table does not exist: " & sTableName)
        Exit Sub
    End If

    'Copy Data
    ProgressBar1.Maximum = iCount
    lbCount.Visible = True

    Log("Copying " & iCount & " rows from table: " & sTableName)

    cmd = New OleDbCommand("SELECT * FROM " & sTableName, cn)

    Dim dr As OleDbDataReader = cmd.ExecuteReader()
    Dim oSchemaRows As Data.DataRowCollection = dr.GetSchemaTable.Rows

    Dim sRow As String
    Dim i As Integer
    Dim iRow As Integer = 0

    'Get Header
    Dim sHeader As String = ""
    For i = 0 To oSchemaRows.Count - 1
        Dim sColumn As String = oSchemaRows(i)("ColumnName")
        If i <> 0 Then
            sHeader += ", "
        End If
        sHeader += sColumn
    Next

    While dr.Read()
        sRow = ""
        For i = 0 To oSchemaRows.Count - 1
            If sRow <> "" Then
                sRow += ", "
            End If

            sRow += GetValueString(dr.GetValue(i))
        Next

        OpenConnections(cn, cnOdbc)
        Dim sSql1 As String = "INSERT INTO " & sTableName _
        & " (" & sHeader & ") VALUES (" & sRow & ")"
        Dim oCmd1 As New OdbcCommand(sSql1, cnOdbc)

        Try
            oCmd1.ExecuteNonQuery()
        Catch ex As Exception
            Log(ex.Message & vbTab & "SQL: " & sSql1)
        End Try

        iRow += 1
        ProgressBar1.Value = iRow
        lbCount.Text = iRow.ToString()
        lbCount.Refresh()
    End While
    dr.Close()

    ProgressBar1.Value = 0
    lbCount.Visible = False
    lbCount.Text = ""

    Log("Finished processing " & sTableName)
End Sub

OpenConnections sub is called regularly just in case the database connection got closed.

VB.NET
Private Sub OpenConnections(ByRef cn As OleDbConnection, ByRef cnOdbc As OdbcConnection)

    If cn.State <> ConnectionState.Open Then
        cn.Open()
    End If

    If cnOdbc.State <> ConnectionState.Open Then
        cnOdbc.Open()
    End If

End Sub

GetMySqlConnectionString function will create MySQL connection string based on user information.

VB.NET
Function GetMySqlConnectionString() As String
    Return "Driver={" & cboDriver.Text & "};Option=3" & _
       ";Server=" & txtServer.Text & _
       ";Port=;Database=" & txtDatabase.Text & _
       ";User=" & txtUser.Text & _
       ";Password=" & txtPassword.Text & ";"
End Function

Log sub will show the progress log to the user.

VB.NET
Private Sub Log(s As String)
    txtLog.Text += s & vbCrLf
    txtLog.Refresh()
End Sub

GetValueString function is used by ExportTable to generate the INSERT INTO statement.

VB.NET
Private Function GetValueString(ByVal obj As Object) As String
    If (IsDBNull(obj)) Then Return "NULL"

    Select Case obj.GetType.FullName

        Case "System.Boolean"
            If (obj = True) Then
                Return "1"
            Else
                Return "0"
            End If

        Case "System.String"
            Dim str As String = obj
            Return "'" + str.Replace("'", "''") + "'"

        Case "System.DateTime"
            Return "STR_TO_DATE('" + obj.ToString() + "','%m/%d/%Y %r')"

        Case "System.Drawing.Image"
            Return "NULL"

        Case "System.Drawing.Bitmap"
            Return "NULL"

        Case "System.Byte[]"
            Return "0x" + GetHexString(obj)

        Case Else
            Return obj.ToString()

    End Select
End Function

GetHexString function will encode binary data into hexadecimal string. This function is called by GetValueString.

VB.NET
Private Function GetHexString(ByRef bytes() As Byte) As String
    Dim sb As New System.Text.StringBuilder
    Dim b As Byte
    Dim i As Integer = 0

    For Each b In bytes
        i += 1
        sb.Append(b.ToString("X2"))
        If i > 10 Then
            Return sb.ToString()
        End If
    Next

    Return sb.ToString()
End Function

GetColumnsTableSqlServer function uses SQL Server INFORMATION_SCHEMA.COLUMNS systems view to create the CREATE TABLE statement.

VB.NET
Private Function GetColumnsTableSqlServer(ByVal sTableName As String, _
ByRef cn As OleDbConnection) As String
    Dim sb As New System.Text.StringBuilder()

    sb.Append("CREATE TABLE " & sTableName & " (" & vbCrLf)

    Dim sSql As String = "select * from INFORMATION_SCHEMA.COLUMNS _
    where TABLE_NAME = '" & PadQuotes(sTableName) & "'"

    Dim cmd As New OleDbCommand(sSql, cn)
    Dim dr As OleDbDataReader = cmd.ExecuteReader()

    Dim i As Integer = 0
    While dr.Read
        Dim sColumn As String = dr.GetValue(dr.GetOrdinal("COLUMN_NAME")).ToString()
        Dim sDataType As String = dr.GetValue(dr.GetOrdinal("DATA_TYPE")).ToString()
        Dim bAllowDBNull As Boolean = dr.GetString(dr.GetOrdinal("IS_NULLABLE")) = "YES"
        Dim sColumnSize As String = dr.GetValue(dr.GetOrdinal_
    ("CHARACTER_MAXIMUM_LENGTH")).ToString()

        'SQL Server to MySql data type converter
        Select Case LCase(sDataType)
            Case "money" : sDataType = "decimal"
            Case "ntext" : sDataType = "text"
            Case "smalldatetime" : sDataType = "datetime"
        End Select

        If sDataType = "decimal" OrElse sDataType = "numeric" Then
            Dim sPrecision As String = dr.GetValue(dr.GetOrdinal_
        ("NUMERIC_PRECISION")).ToString() & ""
            Dim sScale As String = dr.GetValue(dr.GetOrdinal("NUMERIC_SCALE")).ToString() & ""
            sDataType += "(" & sPrecision & ", " & sScale & ")"

        ElseIf sDataType = "text" OrElse sDataType = "image" Then
            sColumnSize = ""
        End If

        If i > 0 Then
            sb.Append(",")
            sb.Append(vbCrLf)
        End If

        sb.Append(PadColumnName(sColumn))
        sb.Append(" " & sDataType)

        If sColumnSize <> "" Then
            sb.Append("(" & sColumnSize & ")")
        End If

        If bAllowDBNull Then
            sb.Append(" NULL")
        Else
            sb.Append(" NOT NULL")
        End If

        i += 1
    End While

    sb.Append(")")

    dr.Close()

    If i = 0 Then
        Return ""
    Else
        Return sb.ToString()
    End If

End Function

GetColumnsTableAccess function uses OleDbDataReader.GetSchemaTable.Rows to create the CREATE TABLE statement for MS Access.

VB.NET
Private Function GetColumnsTableAccess(ByVal sTableName As String, _
    ByRef cn As OleDbConnection) As String

    Dim sb As New System.Text.StringBuilder()
    sb.Append("CREATE TABLE " & PadColumnName(sTableName) & " (" & vbCrLf)

    Dim sSql As String = "select * from " & PadColumnName(sTableName) & " where 0=1"

    Dim cmd As New OleDbCommand(sSql, cn)
    Dim dr As OleDbDataReader = cmd.ExecuteReader()
    Dim oSchemaRows As Data.DataRowCollection = dr.GetSchemaTable.Rows
    Dim sKeyColumns As String = ""
    Dim i As Integer = 0

    For iCol As Integer = 0 To oSchemaRows.Count - 1
        Dim sColumn As String = oSchemaRows(iCol).Item("ColumnName").ToString() & ""
        Dim sColumnSize As String = oSchemaRows(iCol).Item("ColumnSize").ToString() & ""
        Dim sDataType As String = oSchemaRows(iCol).Item("DATATYPE").FullName.ToString()
        Dim bAllowDBNull As Boolean = oSchemaRows(iCol).Item("AllowDBNull") 'Does not always work

        If i > 0 Then
            sb.Append(",")
            sb.Append(vbCrLf)
        End If

        sb.Append(PadColumnName(sColumn))
        sb.Append(" " & PadAccessDataType(sDataType, sColumnSize))

        If bAllowDBNull Then
            sb.Append(" NULL")
        Else
            sb.Append(" NOT NULL")
        End If

        i += 1
    Next

    sb.Append(")")

    dr.Close()

    If i = 0 Then
        Return ""
    Else
        Return sb.ToString()
    End If

End Function

PadAccessDataType function creates a mapping for Access vs. MySQL Datatype.

VB.NET
    Private Function PadAccessDataType_
        (ByVal sDataType As String, ByVal sColumnSize As String) As String

        sDataType = Replace(sDataType, "System.", "")

        Select Case LCase(sDataType)
            Case "string" : sDataType = "VARCHAR"
            Case "int16" : sDataType = "SMALLINT"
            Case "int32" : sDataType = "INT"
            Case "int64" : sDataType = "BIGINT"
        End Select

        If sColumnSize <> "" Then
            Return sDataType & "(" & sColumnSize & ")"
        Else
            Return sDataType
        End If

    End Function

PadQuotes Helper function replaces single quote with double quotes.

VB.NET
Public Function PadQuotes(ByVal s As String) As String
    If s = "" Then
        Return ""
    End If
    Return (s & "").Replace("'", "''")
End Function

PadColumnName Helper function to enclose string to MySQL quotes.

VB.NET
Public Function PadColumnName(ByVal sTable As String) As String
    Return "`" & sTable & "`"
End Function

Here is the code for AppRegistry.vb module.

VB.NET
Imports Microsoft.Win32

Public Class AppRegistry

    Dim sRegKey As String = "SOFTWARE\Krupitsky Labs\CopyTable"
    Dim sRegKey64 As String = "SOFTWARE\Wow6432Node\Krupitsky Labs\CopyTable"

SetRegistryValue sub saves a value into the Windows registry.

VB.NET
Public Sub SetRegistryValue(ByVal sKey As String, ByVal sValue As String)
    'Update registry
    Try
        Dim oKey As RegistryKey = Registry.LocalMachine.OpenSubKey(sRegKey, True)
        If oKey Is Nothing Then
            oKey = Registry.LocalMachine.CreateSubKey(sRegKey)
        End If

        If Not oKey Is Nothing Then
            oKey.SetValue(sKey, sValue)
        End If

    Catch ex As Exception
        MsgBox("Update registry failed: " & ex.Message, , "DeleteBlankPages")
    End Try
End Sub

GetRegValue gets value from the Windows registry.

VB.NET
    Private Function GetRegValue(ByVal sFolder As String, ByVal sKey As String) As String
        Dim oKey As RegistryKey = Registry.LocalMachine.OpenSubKey(sFolder, False)
        Dim sValue As String = ""

        If Not oKey Is Nothing Then
            sValue = oKey.GetValue(sKey)
            oKey.Close()
        End If

        Return sValue
    End Function

End Class 

I hope someone might find this useful.

History

  • 4th May, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)