Getting data from an Excel datatable using OLEDB - vb.net

I have an excel 2007 xlsm file, where on one of the tabs I have several data tables. Using VB.NET, I'm trying to read one table at a time as a named range like so:
Public Function OpeDataFromRange(ByVal Filename as string, ByVal RangeName As String, ByVal bColumnNames As Boolean) as DataTable
' Returns a DataSet containing information from a named range
' in the passed Excel worksheet
Dim sHDR As String
Dim strConn As String
If bColumnNames Then
sHDR = "Yes"
Else
sHDR = "No"
End If
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & Filename & ";Extended Properties=""Excel 12.0 Macro;HDR=" & sHDR & """;"
Dim objConn _
As New System.Data.OleDb.OleDbConnection(strConn)
objConn.Open()
' Create objects ready to grab data
Dim objCmd As New System.Data.OleDb.OleDbCommand( _
"SELECT * FROM [" & RangeName & "]", objConn)
Dim objDA As New System.Data.OleDb.OleDbDataAdapter()
objDA.SelectCommand = objCmd
' Fill DataSet
Dim objDS As New System.Data.DataSet()
objDA.Fill(objDS)
' Clean up and return DataSet
objConn.Close()
return objDS
End Function
But I'm getting the error at Fill command:
The Microsoft Office Access database engine could not find the object 'MyNamedTable1'. Make sure the object exists and that you spell its name and the path name correctly.
I tried to read the entire sheet in the SELECT, and then to fish out my table through objDS.Tables, but then Tables gets loaded with only one table with everything in it.
Any Recommendations?

You cannot use Microsoft.Jet.OLEDB.4.0 with Excel 12.0 you should use Microsoft.ACE.OLEDB.12.0 instead.
Incidentally, you are filling a DataSet but returning a DataTable you need to change one of those.
Personally I prefer to use a DataTable for this but you may prefer a DataSet. If you want to use a DataTable you can...
Dim objDT As New DataTable
objDT.Load(objCmd.ExecuteReader)

Related

Excel VBA ADO SQL connection error - Could not find the object

I got a brilliant answer to my previous question from #Ryan Wildry but I thought I'd ask a different question regarding the same code: here goes.
Background Info
I have a shared (network/server) Excel template file which is both the input file and the data source (although on different sheets). Let's call that Input.xltm.
The code basically picks up a input in a range on Input Sheet, takes the first two letters and finds the closest code from Code Sheet, then populates a UserForm ListBox with the top five results.
The problem
The problem comes when users set off the UserForm and the error usually returns:
Run-time error '-2147467259'
The Microsoft Access database engine could not find the object 'C:\Users\user.name\Documents\Input1'. Make sure the object exists and that you spell its name and the path name correctly.......etc
I think it may have something to do with the fact Excel puts a number after the filename because it's a template file although I don't actually know!
The code
And here's the code:
Public MyConnection As New ADODB.Connection
Public MyRecordset As New ADODB.Recordset
Private Sub UserForm_Initialize()
Dim ColumnName As String: ColumnName = "[Variant code]"
Dim SearchStr As String: SearchStr = Left(Sheets("Input Sheet").Range("B4").Value2, 2)
Dim dbstring As String
dbstring = ThisWorkbook.FullName
Application.ScreenUpdating = False
If MyConnection.State <> adStateOpen Then
With MyConnection
.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbstring & _
";Extended Properties='Excel 12.0 Xml;HDR=YES;IMEX=1';"
.Open
End With
End If
If MyRecordset.State = adStateOpen Then MyRecordset.Close
MyRecordset.Open "Select top 5 " & ColumnName & " from [Code Sheet$] where " & ColumnName & _
" like '%" & SearchStr & "%'", MyConnection, adOpenForwardOnly, adLockReadOnly
Me.ListBox1.Clear
If Not MyRecordset.EOF Then MyRecordset.MoveFirst
Application.ScreenUpdating = True
Do Until MyRecordset.EOF
Me.ListBox1.AddItem MyRecordset.Fields(0).Value
MyRecordset.MoveNext
Loop
End Sub
I just need everyone who accesses the file through the server to be able to pick up the correct data source (which is only in the next sheet) and populate the ListBox.
I'd be thankful for any suggestions! Thanks
#UPDATE
I have checked, now if you open (and then save) the actual template file so there's no '1' after the file name, then the code works as expected. It's only when the template is opened normally and the number automatically appended that it stops working.
It seems that you do not make early-binding for MyConnection and MyRecordset first.
You can make a late-binding by
step 1.
Change
Public MyConnection As New ADODB.Connection
Public MyRecordset As New ADODB.Recordset
to
Public MyConnection As object
Public MyRecordset As object
.
step 2.
Add
Set MyConnection = createobject("adodb.connection")
Set MyRecordset = createobject("adodb.recordset")
before If MyConnection.State <> adStateOpen Then

Copying data from excel into access with VBA?

Im trying to update an Access database with new code to add to one aggregate list of scan entries. The macro needs to open the access file, copy the range from excel and paste it at the bottom of the database to add to the already existing records. Then save the access .accdb file and then close. Any tips particularly on the copying and pasting data portion?
Use ACE.OLEDB. Create an SQL INSERT STATEMENT. Code would look something like below:
Sub Test()
accessFilePath = "C:\someDB.accdb"
Call ExecuteSQLCmd("INSERT INTO `" & accessFilePath & "`.`Table` (col1,col2,col3) SELECT col1,col2,col3 FROM [Sheet1$]", accessFilePath)
End Sub
Sub ExecuteSQLCmd(cmd As String, accessFilePath as String )
Dim cnn As ADODB.Connection
Dim sql As String
Set cnn = New ADODB.Connection
cnn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & accessFilePath & ";Persist Security Info=False;"
If Not (cnn Is Nothing) Then
'Execute Sql
cnn.Execute (cmd)
'Close
cnn.Close
End If
Set cnn = Nothing
End Sub

Unable to read data from a CSV using ADO due to the driver thinking I am working with integers/number and showing nulls instead of text

I am trying to use the ADO to read in a series of text files into a worksheet. I am running into problems when the majority of the data in a specific column are integers. It will give null values (blank cells) when it reaches a String.
According to microsoft support (Ado mixed data tyes) this is a common thing and the solution is to set the IMEX = 1. I tried this however it didn't work.
I have been searching others threads looking for the answer and came across this answer (other thread) where the author says to change TypeGuessRows to "get the Jet to detect whether a mixed types situation exists and trick the Jet into detecting a certain data type." However, this hasn't worked either.
Below is my VBA code. Any help would be appreciated
Sub query_text_file(WorkingSheet As String, Col As String, Row As Integer, fileName As String, firstOrLast As Integer)
Dim strPath As String
Dim ws As Worksheet
strToolWkbk = fileName
strPath = ThisWorkbook.Path & "\Excel_Barcode_Files"
Set ws = Worksheets(WorkingSheet)
'Need to reference the:
' Microsoft ActiveX Data Objects 2.5 Library
Dim s_rst As ADODB.Recordset
Dim s_cnn As ADODB.Connection 's for sub connection
Dim intRow As Integer
Const adOpenStatic = 3
Const adLockOptimistic = 3
Const adCmdText = &H1
Set s_cnn = New ADODB.Connection
s_cnn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & strPath & ";" _
& "Extended Properties=""text;HDR=Yes;IMEX=1;TypeGuessRows=12;FMT=Delimited"";"
s_cnn.Open
Set s_rst = New ADODB.Recordset
strSQL = "SELECT * FROM " & strToolWkbk
s_rst.Open strSQL, _
s_cnn, adOpenStatic, adLockOptimistic, adCmdText
intRow = Row
s_rst.MoveFirst
Do Until s_rst.EOF
ws.Range(Col & intRow) = s_rst(0)
ws.Range(Chr(Asc(Col) + 1) & intRow) = s_rst(1)
intRow = intRow + 1
s_rst.MoveNext
Loop
s_rst.Close
s_cnn.Close
Set s_rst = Nothing
Set s_cnn = Nothing
End Sub
Here is a sample text file. The code reads in everything except the "P"
test test
P,0
1,1
5,2
6,3
Basically, don't rely on the registry entries as explained here on MSDN.
You need to create a Schema.ini file and put it in the same folder as all your text files. In the Schema.ini you specify the type for all columns you may have in your text files - it's just a much safer option to do that explicitly rather than have the driver work out the correct types for columns...
Say you have some txt files on your desktop, open Notepad and copy paste the below - make sure you adjust the [test.txt] part to match the name of your actual txt file and save it as: Schema.ini
[test.txt]
Format=CSVDelimited
Col1=Column1 Text
Col2=Column2 Text
Make sure you add another slash at the end of the parth in the strPath (also indicated in the article)
strPath = ThisWorkbook.Path & "\Excel_Barcode_Files\"
*Keep in mind that I am working in a different location to yours - I am using my Desktop for this example and my text file is named test.txt
Now, that you have a Schema.ini you can modify the connection string and take out some parameters which are not required because they exists in the Schema.ini
So bascially an SSCCE based on the above assumptions would be:
Sub Main()
Cells.ClearContents
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim thePath As String
thePath = "C:\Users\" & Environ("USERNAME") & "\Desktop\"
cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & thePath & ";" _
& "Extended Properties=""text;HDR=No;"""
cn.Open
Dim sql As String
sql = "SELECT * FROM test.txt"
' populate the recordset
rs.Open sql, cn, adOpenStatic, adLockOptimistic, &H1
' copy the recordset starting at Range("A1") - assuming there are no headers - see HDR = No;
Range("A1").CopyFromRecordset rs
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
Now after running this you should see all the values including the missing P:

import excel data

I'm able to import my excel into datagridview successfully but only there is a little problem. There is only one column in my excel file and 10 rows. All 9 rows are in numbers and only the last row is a string.
all 10 show up in my datagridview but the last row is blank.
below is my function to open excel file
Public Sub OpenExcel(ByVal sFileName As String)
Dim ConnectionStringTemplate As String = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source={0};" & _
"Extended Properties=""Excel 8.0;" & _
"HDR=No;IMEX=1"""
Dim ConnectionString As String = String.Format(ConnectionStringTemplate, sFileName)
Dim sqlSelect As String = "SELECT * FROM [Sheet1$];" 'Where you have a sheet named Sheet1
Dim WorkBook As New DataSet
Dim ExcelAdapter As System.Data.Common.DataAdapter = New System.Data.OleDb.OleDbDataAdapter(sqlSelect, ConnectionString)
ExcelAdapter.Fill(WorkBook)
frmMain.dgvCompose.DataSource = WorkBook.Tables(0)
frmMain.staRecords.Text = "Total Records - " & frmMain.dgvCompose.RowCount
End Sub
You can enumarate datatable's rows with foreach and add all items to array or list.
It is more efficent than giving workbook's table to datasource directly. And if string row is exist, you can see when debugging.

Closing an OLE DB connection opened from within a function

Backed myself in a corner....
Used a piece of code I found on the web and can't figure out how to close this connection. The returned OleDbcommand objCommand remains open after processing. I need to it close so I can delete the file after I have pulled the data from it. (Don't want them hanging around on the server.)
This has to be easier than the 100 of lines of code that I have tried to do this. This function opens the connection.
Protected Function ExcelConnection() As OleDbCommand
Dim fileName As String = Session("newUploadedFile")
' Connect to the Excel Spreadsheet
Dim xConnStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & Server.MapPath(String.Format("~/Upload/{0}", fileName)) & ";" & _
"Extended Properties=Excel 8.0;"
' create your excel connection object using the connection string
Dim objXConn As New OleDbConnection(xConnStr)
objXConn.Open()
' use a SQL Select command to retrieve the data from the Excel Spreadsheet
' the "table name" is the name of the worksheet within the spreadsheet
' in this case, the worksheet name is "Sheet1" and is expressed as: [Sheet1$]
Dim objCommand As New OleDbCommand("SELECT Name FROM [Sheet1$]", objXConn)
Return objCommand
End Function
I have tried...
ExcelConnection.connection.close()
along with about 40 other attempts to recreate the Command and then close it.
Could really use some help on this one.
This is probably not the best way to do this, but if you really must do it this way consider defining and opening the connection in the calling routine and passing it into this routine as a parameter. It can then be closed in the calling routing, thus...
Sub Main()
Dim xConnStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & Server.MapPath(String.Format("~/Upload/{0}", fileName)) & ";" & _
"Extended Properties=Excel 8.0;"
Dim objXConn As New OleDbConnection(xConnStr)
objXConn.Open()
Dim ObjCommand As New OleDbCommand = ExcelConnection(objXConn)
'Whatever other operations you want to do with your returned OleDbCommand
...
objXConn.Close()
End Sub
Function ExcelConnection(PassedConnection As OleDbConnection) As OleDbCommand
Dim fileName As String = Session("newUploadedFile")
Dim objCommand As New OleDbCommand("SELECT Name FROM [Sheet1$]", PassedConnection)
Return objCommand
End Function
I posted something similar to this here... Best fastest way to read an Excel sheet