Import Excel data to SQL Server table using VB.Net application - sql-server

I found several articles on this topic but the issues are different. I'm trying to import data from an Excel 2016 file into SQL Server 2017 using a VB.net application, so that the end-users need not to have SQL Server installed on their machines. I ran the application in debug mode to identify the issue.
Again I read articles on various instances related to the following error:
The 'Microsoft.ACE.OLEDB.15.0' provider is not registered on the local machine
My code:
Dim TheData As DataTable = GetExcelData(OpenFileDialog1)
Private Function GetExcelData(File As OpenFileDialog) As DataTable
Dim oleDbConnection As New OleDbConnection
Try
OpenOLEDBConnections(oleDbConnection, "Provider=Microsoft.ACE.OLEDB.15.0;Data Source=" & File.InitialDirectory & _
File.FileName & "; ;Extended Properties=""Excel 12.0 XML;HDR=YES;""")
Dim oleDbDataAdapter As OleDbDataAdapter = New OleDbDataAdapter("Select '' as ID, 'AL' as Supplier, LTRIM(RTRIM(GRADE)) AS GRADE, [ship date] as shipdate, LTRIM(RTRIM(coil)) AS coil, L15*1000 AS L15, H15*1000 AS H15 FROM [sheet1$] where COIL is not null group by GRADE, [SHIP DATE], COIL, L15, H15", oleDbConnection)
Dim dataTable As DataTable = New DataTable()
oleDbDataAdapter.Fill(dataTable)
oleDbConnection.Close()
Return dataTable
Catch ex As Exception
oleDbConnection.Close()
Return Nothing
End Try
End Function
Private Sub OpenOLEDBConnections(ByRef cnData As OleDbConnection, cnDataConstr As String)
If Not cnData.State = ConnectionState.Closed Then cnData.Close()
If cnData.State = ConnectionState.Closed Then
cnData.ConnectionString = cnDataConstr
cnData.Open()
End If
End Sub

Not that this is the silver bullet, but i dont like the look of your connection string. There appears to be a ";" out of place. Try this one
Dim XLSFile As String = "C:\DATA\test.xlsx"
Dim connstring As String = "Provider=Microsoft.ACE.OLEDB.12.0;" &
"Data Source=" & XLSFile & ";Extended Properties=""Excel 12.0 Xml;HDR=YES;"""

Try installing ACE provider from the following link
https://www.microsoft.com/en-us/download/confirmation.aspx?id=13255
How to import Excel into SQL Server, check out on
https://www.red-gate.com/simple-talk/dotnet/c-programming/office-development-in-visual-studio/
Or try using this snippet
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.OleDb
Imports System.Data.SqlClient
Imports System.Runtime.InteropServices
Imports Excel = Microsoft.Office.Interop.Excel
Public Module Program
Public Sub Main()
Dim officeType = Type.GetTypeFromProgID("Excel.Application")
If officeType Is Nothing Then
Console.WriteLine("Sorry, Excel must be installed!")
Console.WriteLine("Press any key to exit")
Console.ReadLine()
Return
End If
Const fileToRead As String = "C:\TMP\FirstTest.xlsx"
Dim xlApp As Excel.Application = Nothing
Dim workbooks As Excel.Workbooks = Nothing
Dim xlWorkBook As Excel.Workbook = Nothing
Dim sheets As Excel.Sheets = Nothing
Dim t1 As Excel.Worksheet = Nothing
Try
xlApp = New Excel.Application()
Console.WriteLine($"Trying to open file {fileToRead}")
workbooks = xlApp.Workbooks
xlWorkBook = workbooks.Open(fileToRead, 0, True, 5, "", "", True, Origin:=Excel.XlPlatform.xlWindows, Delimiter:=vbTab, Editable:=False, Notify:=False, Converter:=0, AddToMru:=True, Local:=1, CorruptLoad:=0)
sheets = xlApp.ActiveWorkbook.Sheets
Dim dic = New List(Of String)()
For Each mSheet In sheets
dic.Add($"[{t1.Name}$]")
Next
Using myConnection = New OleDbConnection($"Provider=Microsoft.Ace.OLEDB.12.0;Data Source={fileToRead};Extended Properties='Excel 12.0 Xml;HDR = YES;'")
Using dtSet = New DataSet()
For Each s In dic
Console.WriteLine($" Processing {s} table")
Dim myCommand = New OleDbDataAdapter($"select * from {s};", myConnection)
myCommand.TableMappings.Add("Table", s)
myCommand.Fill(dtSet)
Next
For Each t As DataTable In dtSet.Tables
Console.WriteLine($" Table {t.TableName} has {t.Rows.Count} records")
Next
End Using
End Using
dic = Nothing
Console.WriteLine("Successfully imported!")
Console.WriteLine("After closing Console Windows start Task Manager and be sure that Excel instance is not there!")
Console.WriteLine("Press any key to exit")
Console.ReadLine()
Catch e As Exception
Console.WriteLine($"Error importing from Excel : {e.Message}")
Console.ReadLine()
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
If sheets IsNot Nothing Then
Marshal.FinalReleaseComObject(sheets)
sheets = Nothing
End If
xlWorkBook.Close()
If xlWorkBook IsNot Nothing Then
Marshal.FinalReleaseComObject(xlWorkBook)
xlWorkBook = Nothing
End If
xlApp.Quit()
If xlApp IsNot Nothing Then
Marshal.FinalReleaseComObject(xlApp)
xlApp = Nothing
End If
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
End Sub
End Module

After I spent a day and half, I somehow managed to resolve the issue by going through the following link and tried the option mentioned by Manju K Gowda Monday, May 14, 2012 12:14 PM
Microsoft.ACE.OLEDB.15.0 and also modified my connection string provider from OLEDB 15.0 to 12.0.
To be precise, while building the solution either in Debug or release mode, keep the Configuration Manager to x86 instead of AnyCPU

Related

Query Timeout Expired when updating SQL Server from VB,NET

I'm updating SQL Server from VB.NET and keep getting the 'Query Timeout Error', I have lot's of sub routines that I run in sequence that look like the following:
Public Shared Sub Update_DailyRatings()
Dim stallStats As String = ""
Dim win As Integer = 0
Dim mSplit As Array
Dim cn As OleDbConnection = New OleDbConnection(MainForm.connectStringPublic)
cn.Open()
Dim selectString As String = "Select * FROM DailyRatings"
Dim cmd As OleDbCommand = New OleDbCommand(selectString, cn)
Dim reader As OleDbDataReader = cmd.ExecuteReader()
While (reader.Read())
stallStats = Get_Stall_Stats(reader("Track").ToString, CInt(reader("Stall")), CDbl(reader("Distance")))
If stallStats = "" Then
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = 999 WHERE Horse = '" & reader("Horse").ToString & "'")
Else
mSplit = Split(stallStats, ",")
win = mSplit(0)
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = " & win & " WHERE Horse = '" & reader("Horse").ToString & "'")
End If
End While
reader.Close()
cn.Close()
End Sub
The NonQuery sub looks like this:
Public Sub NonQuery(ByVal SQL As String)
Dim query As String = SQL
Try
Dim cn3 As OleDbConnection = New OleDbConnection(connectStringPublic)
cn3.Open()
Dim cmd As OleDbCommand = New OleDbCommand(query, cn3)
cmd.CommandTimeout = 90
cmd.ExecuteNonQuery()
cn3.Close()
cn3.Dispose()
cmd.Dispose()
OleDbConnection.ReleaseObjectPool()
Catch e As System.Exception
Clipboard.SetText(query)
MsgBox(e.Message)
Finally
End Try
End Sub
As you can see I've been trying ideas to fix this that I found in other threads such as extending the timeout and using the Dispose() and ReleaseObjectPool() methods but it hasn't worked, I still get query timeout error at least once when running all my subs in sequence, it's not always the same sub either.
I recently migrated from Access, this never used to happen with Access.
If you are dealing with Sql Server why are you using OleDb? I guessed that is was really access.
While your DataReader is open, your connection remains open. With the amount of processing you have going on, it is no wonder that your connection is timing out.
To begin, connections and several other database objects need to be not only closed but disposed. They may contain unmanaged resources which are released in the .Dispose method. If you are using an object that exposes a .Dispose method use Using...End Using blocks. This will take care of this problem even if there is an error.
Actually you have 2 distinct operations going on. First you are retrieving DailyRatings and then you are updating DailyRatings base on the data retrieved. So we fill a Datatable with the first chunk of data and pass it off to the second operation. Our first connection is closed and disposed.
In operation 2 we create our connection and command objects just as before except now our command has parameters. The pattern of the command is identical for every .Execute, only the values of the parameters change. This pattern allows the database, at least in Sql Sever, to cache a plan for the query and improve performance.
Public Shared Function GetDailyRatings() As DataTable
Dim dt As New DataTable
Using cn As New OleDbConnection(MainForm.connectStringPublic),
cmd As New OleDbCommand("Select * FROM DailyRatings", cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
Public Sub UpdateDailyRatings()
Dim dt = GetDailyRatings()
Using cn As New OleDbConnection(connectStringPublic),
cmd As New OleDbCommand("UPDATE DailyRatings SET StallWin = #Stall WHERE Horse = #Horse")
cmd.Parameters.Add("#Stall", OleDbType.Integer)
cmd.Parameters.Add("#Horse", OleDbType.VarChar)
cn.Open()
For Each row As DataRow In dt.Rows
cmd.Parameters("#Horse").Value = row("Horse").ToString
Dim stallStats As String = Get_Stall_Stats(row("Track").ToString, CInt(row("Stall")), CDbl(row("Distance")))
If stallStats = "" Then
cmd.Parameters("#Stall").Value = 999
Else
cmd.Parameters("#Stall").Value = CInt(stallStats.Split(","c)(0))
End If
cmd.ExecuteNonQuery()
Next
End Using
End Sub
Private Function GetStallStats(Track As String, Stall As Integer, Distance As Double) As String
Dim s As String
'Your code here
Return s
End Function
Note: OleDb does not pay attention to parameters names. It is the order that they appear in the query statement must match the order that they are added to the Parameters collection.
It's possible that OleDbDataReader is locking your table or connection as it get the data with busy connection. You can store the data in a DataTable by using OleDbDataAdapter and loop through it to run your updates. Below is the snippet how your code would look like:
Dim cmd As OleDbCommand = New OleDbCommand(selectString, cn)
Dim adapter As OleDbDataAdapter = New OleDbDataAdapter(cmd)
Dim dt As New DataTable()
adapter.Fill(dt)
For Each reader As DataRow In dt.Rows
stallStats = Get_Stall_Stats(reader("Track").ToString, CInt(reader("Stall")), CDbl(reader("Distance")))
If stallStats = "" Then
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = 999 WHERE Horse = '" & reader("Horse").ToString & "'")
Else
mSplit = Split(stallStats, ",")
win = mSplit(0)
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = " & win & " WHERE Horse = '" & reader("Horse").ToString & "'")
End If
Next
cn.Close()

Auto complete textbox from mdf (master SQL Server database file)

I'm trying to use an autocomplete textbox, fed from a SQL Serve .mdf database file.
This is my code:
Dim cmd As New SqlCommand("Select col_name FROM college ", cn)
If cn.State = ConnectionState.Closed Then cn.Open()
Dim ds As New DataSet
Dim sqda As New SqlDataAdapter(cmd)
sqda.Fill(ds, "college")
Dim col As New AutoCompleteStringCollection
Dim i As Integer
For i = 0 To ds.Tables(0).Rows.Count - 1
col.Add(ds.Tables(0).Rows(i)("col_name").ToString())
Next
TextBox1.AutoCompleteSource = AutoCompleteSource.CustomSource
TextBox1.AutoCompleteCustomSource = col
TextBox1.AutoCompleteMode = AutoCompleteMode.Suggest
Since I am using SQL Server, the data not showing in the list.
I think its needs to use character N like
Insert data into SQL Server as:
cmd = New SqlCommand("insert into college (col_name) values (N'" & TextBox1.Text.Trim & "')", cn)
Keep your database objects local so you can control the closing and disposing. Using...End Using blocks handle this even if there is an error.
Always use parameters to avoid Sql injection.
In .net Char supports Unicode so strings (which are arrays of Char) also support Unicode. As long as the field in Sql Server is of type NVarChar. I don't see a problem
Private Sub SetUpAutoComplete()
Dim dt As New DataTable
Using cn As New SqlConnection("Your connection string")
Using cmd As New SqlCommand("Select col_name FROM college ", cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
Dim col As New AutoCompleteStringCollection
For Each row As DataRow In dt.Rows
col.Add(row("col_name").ToString)
Next
'Just to check if we have some data
Debug.Print(col.Count.ToString)
TextBox3.AutoCompleteSource = AutoCompleteSource.CustomSource
TextBox3.AutoCompleteCustomSource = col
TextBox3.AutoCompleteMode = AutoCompleteMode.Suggest
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
SetUpAutoComplete()
End Sub
Private Sub InsertCollege()
Using cn As New SqlConnection("Your Connection string")
Using cmd As New SqlCommand("insert into college (col_name) values (#Name)", cn)
cmd.Parameters.Add("#Name", SqlDbType.NVarChar).Value = TextBox1.Text.Trim
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Using
End Sub

SQL Image to Crystal Report

I want to pull image data from SQL Server 2008 Express and display it directly in a Crystal Report. I am using Visual Studio 2012 along with Crystal Reports for Visual Studio (also a full version of SAP Crystal Reports 2013).
I have attempted to use samples from Google/SO searches but I seem to be missing at least one key element. In my project I have a dataset with one field which is of System.Byte(), a Crystal Report, and a viewer.
Here is the snippet of code which queries the database, reads the memorystream, and puts the image data into the dataset.
Dim ds As New DataSet1
Dim row As DataRow
Dim objConn As New SqlConnection(DatabaseConnection.CTLDataConnectionString)
Dim objCommand As SqlCommand = objConn.CreateCommand
objCommand.CommandText = "SELECT Content FROM Report WHERE HandlingUnitID = " & HandlingUID
Dim dr As SqlDataReader = Nothing
If objConn.State = ConnectionState.Closed Then objConn.Open()
dr = objCommand.ExecuteReader
If dr.HasRows Then
dr.Read()
Dim b() As Byte = DirectCast(dr("Content"), Byte())
Using ms As New MemoryStream(b)
ds.DataTable1.Rows.Add(b)
End Using
End If
Dim rpt As New ShipLabel
rpt.SetDataSource(ds.Tables("DataTable1"))
Dim frm As New CRviewer
frm.CRvwr.ReportSource = rpt
frm.ShowDialog()
This will bring up the Crystal Viewer but it is a blank report. I want to know if I have the correct sequence for getting the data, and if I am storing it correctly in the dataset.
After a lot more reading/researching I have the following snippets of code which appear to have the correct sequencing, and correctly storing the data in the dataset. This snippet is from a cellclick event.
Dim ds As New DataSet1
Dim row As DataRow
Dim img As Bitmap
Dim objConn As New SqlConnection(DatabaseConnection.CTLDataConnectionString)
Dim objCommand As SqlCommand = objConn.CreateCommand
objCommand.CommandText = "SELECT Content FROM Report WHERE HandlingUnitID = " & HandlingUID
Dim dr As SqlDataReader = Nothing
If objConn.State = ConnectionState.Closed Then objConn.Open()
dr = objCommand.ExecuteReader
If dr.HasRows Then
dr.Read()
Dim b() As Byte = dr("Content")
Using ms As New MemoryStream(b)
img = Image.FromStream(ms)
b = ConvertImageToByte(img)
ds.DataTable1.Rows.Add(b)
End Using
End If
objConn.Close()
Dim rpt As New ShipLabel
rpt.SetDataSource(ds.Tables("DataTable1"))
rpt.SetParameterValue("JobNumber", OrderNumber.ToString)
Dim frm As New CRviewer
frm.CRvwr.ReportSource = rpt
frm.ShowDialog()
And this function:
Public Shared Function ConvertImageToByte(ByVal Value As Image) As Byte()
If Value IsNot Nothing Then
Dim fs As MemoryStream = New MemoryStream()
CType(Value, Bitmap).Save(fs, ImageFormat.Jpeg)
Dim retval As Byte() = fs.ToArray()
fs.Dispose()
Return retval
End If
Return Nothing
End Function

Read values from SQL Server database using vba

i am using vba studio and would like to read some values from a sql database.
Here is my code:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Public Class log
Dim usuariovb As String
Dim passwordvb As String
Private Sub exe_log_Click(sender As Object, e As EventArgs) Handles exe_log.Click
usuariovb = txt_user.Text
passwordvb = txt_pw.Text
Dim sConnectionString As String = "Server=MXD071352\SQLEXPRESS; Database=CALLC; User Id=sa;Password=Kernmic1"
Using objConn As New SqlConnection(sConnectionString)
Dim comando As New SqlCommand("SELECT ContraseƱa from Usuarios WHERE Usuario = '" & usuariovb & "';", objConn)
objConn.Open()
Dim lector As SqlDataReader = comando.ExecuteReader()
Try
While lector.Read()
Console.WriteLine(String.Format("{0}, {1}", lector(0), lector(2)))
End While
Finally
lector.Close()
End Try
End Using
End Sub
Could you please help me, since I am not getting results

SqlBulkCopy doesn't copy all the rows from Excel sheet

I developed a Windows app to import Excel file and write the data into a SQL Server database table. The application works well but it starts writing from the line 27 or 30 and sometimes from line 29 of the Excel sheet. I need all the rows to be written to the database table from line 1 to line 4500.
My code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim fdlg As OpenFileDialog = New OpenFileDialog
fdlg.Title = "Open File Dialog"
fdlg.InitialDirectory = "C:\"
fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*"
fdlg.FilterIndex = 2
fdlg.RestoreDirectory = True
If fdlg.ShowDialog() = Windows.Forms.DialogResult.OK Then
ExcelFileName = fdlg.FileName
End If
'Excel 2007
Dim ExcelConnection As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + ExcelFileName + ";Extended Properties=""Excel 12.0 Xml;HDR=No;""")
Try
ExcelConnection.Open()
Catch ex As Exception
End Try
Dim expr As String = "SELECT * FROM [Sheet1$]"
Dim objCmdSelect As OleDbCommand = New OleDbCommand(expr, ExcelConnection)
Dim objDR As OleDbDataReader
Dim SQLconn As New SqlConnection()
Dim ConnString As String = "Data Source=My_PC_Name;Initial Catalog=myDatabase;Integrated Security=True"
SQLconn.ConnectionString = ConnString
SQLconn.Open()
Using bulkCopy As SqlBulkCopy = New SqlBulkCopy(SQLconn)
bulkCopy.DestinationTableName = "ItemDetails"
Try
objDR = objCmdSelect.ExecuteReader
If objDR.HasRows = True Then
bulkCopy.WriteToServer(objDR)
MessageBox.Show("You Successfuly import the excel file")
objDR.Close()
SQLconn.Close()
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
End Sub
SQL is per definition not ordered. So to guarantee the order you will have to add a helper column (say column A) with your row_id. YOu can then use that later to select your data ordered.

Resources