How do I select a variable with a space in the name using VBA from a CSV file via ADO? - sql

Sub GetTextFileData(strSQL As String, strFolder As String, rngTargetCell As Range)
' example: GetTextFileData "SELECT * FROM filename.txt", _
"C:\FolderName", Range("A3")
' example: GetTextFileData "SELECT * FROM filename.txt WHERE fieldname = 'criteria'", _
"C:\FolderName", Range("A3")
Dim cn As ADODB.Connection, rs As ADODB.Recordset, f As Integer
If rngTargetCell Is Nothing Then Exit Sub
Set cn = New ADODB.Connection
On Error Resume Next
cn.Open "Driver={Microsoft Text Driver (*.txt; *.csv)};" & _
"Dbq=" & strFolder & ";" & _
"Extensions=asc,csv,tab,txt;"
On Error GoTo 0
If cn.State <> adStateOpen Then Exit Sub
Set rs = New ADODB.Recordset
On Error Resume Next
rs.Open strSQL, cn, adOpenForwardOnly, adLockReadOnly, adCmdText
On Error GoTo 0
If rs.State <> adStateOpen Then
cn.Close
Set cn = Nothing
Exit Sub
End If
' the field headings
For f = 0 To rs.Fields.count - 1
rngTargetCell.Offset(0, f).Formula = rs.Fields(f).name
Next f
rngTargetCell.Offset(1, 0).CopyFromRecordset rs ' works in Excel 2000 or later
'RS2WS rs, rngTargetCell ' works in Excel 97 or earlier
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
I can't remember where I got the above code but it's really good. It allows me to use SQL on a CSV and extract the data I need.
Now I have a csv file where the column names can have a space in it and have other symbols such as % in the name. Now the code doesn't work. How do I tell VBA to select a column with a name such as "Swing %" the code below didn't work
GetTextFileData "select Swing % as swing_pct from data.csv", "c:\somewhere", Range("A1")

Try [] escaping;
select [Swing %] as ...
Note: Alex K mentioned this in the comment.

Related

Using VBA to query a VIEW from SQL Server

I am trying to create a VBA script that will pull the results from a View (SELECT * FROM view_name) from the RecordSet.Source property, but when attempted, my CloseConnection error handler keeps getting caught. I can get results from a table using a simple query like SELECT * FROM tbl_name with no issues.
Below is the code I am using. Note: my Const variable has the Provider and Database information removed.
I guess it really comes down to is it even possible to get results from a View like I would from a table?
Option Explicit
Const ConStrMSSQL As String = _
"Provider=provider_name;Database=database_name;Trusted_Connection=yes;"
Sub test()
Dim formConnect As ADODB.connection
Dim formData As ADODB.recordSet
Dim formField As ADODB.Field
Set formConnect = New ADODB.connection
Set formData = New ADODB.recordSet
formConnect.ConnectionString = ConStrMSSQL
formConnect.Open
On Error GoTo CloseConnection
With formData
.ActiveConnection = formConnect
.Source = "SELECT * FROM v_data_extract_658"
.LockType = adLockReadOnly
.CursorType = adOpenForwardOnly
.Open
End With
On Error GoTo CloseRecordset
Sheets("test").Range("A1").Select
For Each formField In formData.Fields
ActiveCell.Value = formField.Name
ActiveCell.Offset(0, 1).Select
Next formField
Sheets("test").Range("A2").CopyFromRecordset formData
On Error GoTo 0
CloseRecordset:
formData.Close
CloseConnection:
formConnect.Close
End Sub
This is the error message:
run-time error 2147467259 (80004005): unknown token received from SQL Server
I think the big issue here is that you haven't defined a Command Object.
I somewhat put this together "freehand" and for certain, didn't test it but it should get you to where you need to go.
Sub test()
On Error GoTo ErrorHandle:
Dim formConnect As ADODB.Connection
Set formConnect = New ADODB.Connection
formConnect.ConnectionString = ConStrMSSQL
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
formConnect.Open
With cmd
.ActiveConnection = formConnect
.CommandType = adCmdText
.CommandText = "SELECT * FROM v_data_extract_658"
.CommandTimeout = 30
End With
Dim formData As ADODB.Recordset
Set formData = New ADODB.Recordset
formData.Open cmd, , adOpenStatic, adLockReadOnly
Sheets("test").Range("A1").Select
Dim formField As ADODB.Field
For Each formField In formData.Fields
ActiveCell.value = formField.Name
ActiveCell.Offset(0, 1).Select
Next formField
Range("A2").CopyFromRecordset formData
On Error GoTo 0
Cleanup:
If Not formData Is Nothing Then
If formData.State <> adStateClosed Then formData.Close
Set formData = Nothing
End If
If Not formConnect Is Nothing Then
If formConnect.State <> adStateClosed Then formConnect.Close
Set formConnect = Nothing
End If
Set cmd = Nothing
Exit Sub
ErrorHandle:
MsgBox Err.Description
'Do whatever else is needed to respond to errors.
Resume Cleanup
End Sub
Using Excel & VBA to fetch dta from SLQ Server is quite easy (not always, but these days).
Sub ADOExcelSQLServer()
' Carl SQL Server Connection
'
' FOR THIS CODE TO WORK
' In VBE you need to go Tools References and check Microsoft Active X Data Objects 2.x library
'
Dim Cn As ADODB.Connection
Dim Server_Name As String
Dim Database_Name As String
Dim User_ID As String
Dim Password As String
Dim SQLStr As String
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
Server_Name = "EXCEL-PC\SQLEXPRESS" ' Enter your server name here
Database_Name = "NORTHWND" ' Enter your database name here
User_ID = "" ' enter your user ID here
Password = "" ' Enter your password here
SQLStr = "SELECT * FROM [Customers]" ' Enter your SQL here
Set Cn = New ADODB.Connection
Cn.Open "Driver={SQL Server};Server=" & Server_Name & ";Database=" & Database_Name & _
";Uid=" & User_ID & ";Pwd=" & Password & ";"
rs.Open SQLStr, Cn, adOpenStatic
' Dump to spreadsheet
For iCols = 0 To rs.Fields.Count - 1
Worksheets("Sheet1").Cells(1, iCols + 1).Value = rs.Fields(iCols).Name
Next
With Worksheets("sheet1").Range("a2:z500") ' Enter your sheet name and range here
'.ClearContents
.CopyFromRecordset rs
End With
' Tidy up
rs.Close
Set rs = Nothing
Cn.Close
Set Cn = Nothing
End Sub
As an aside, you can try this as well (please change to suit your specific setup/configuration)...
Sub Working2()
Dim con As Connection
Dim rst As Recordset
Dim strConn As String
Set con = New Connection
strConn = "EXCEL-PC\SQLEXPRESS;Database=Northwind;Trusted_Connection=True"
con.Open strConn
'Put a country name in Cell E1
Set rst = con.Execute("Exec dbo.MyOrders '" & ActiveSheet.Range("E1").Text & "'" & ActiveSheet.Range("E2").Text & "'")
'The total count of records is returned to Cell A5
ActiveSheet.Range("A5").CopyFromRecordset rst
rst.Close
con.Close
End Sub
Please see the link below for more details.
https://www.excel-sql-server.com/excel-sql-server-import-export-using-vba.htm#Excel%20Data%20Export%20to%20SQL%20Server%20Test%20Code

How to pass the table name to sql query in VBA

I'm using the below code to load a table from an MDB database to an excel worksheet. I'm trying to define the table name as variable and pass it to query but I'm getting an error with the code below. How can I do this in VBA?
Public Sub ReadMdb()
Dim cn As Object, rs As Object
Dim intColIndex As Integer
Dim DBFullName As String
Dim TargetRange As Range
DBFullName = Application.GetOpenFilename()
On Error GoTo Oops
Application.ScreenUpdating = False
Set TargetRange = Sheets("Sheet1").Range("A1")
Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & DBFullName
Set rs = CreateObject("ADODB.Recordset")
Dim tableName As String
tableName = "Students"
rs.Open "SELECT * FROM tableName", cn, , , adCmdText
' Write the field names
For intColIndex = 0 To rs.Fields.Count - 1
TargetRange.Offset(1, intColIndex).Value = rs.Fields(intColIndex).Name
Next
' Write recordset
TargetRange.Offset(1, 0).CopyFromRecordset rs
LetsContinue:
Application.ScreenUpdating = True
On Error Resume Next
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
On Error GoTo 0
Exit Sub
Oops:
MsgBox "Error Description :" & Err.Description & vbCrLf & _
"Error at line :" & Erl & vbCrLf & _
"Error Number :" & Err.Number
Resume LetsContinue
End Sub
you can insert table name into SQL:
rs.Open "SELECT * FROM [" & tableName & "]", cn, , , adCmdText
Considering your returning all the rows and all the columns, you could use the command type adCmdTable then you don't need any SQL you simply name the table that you want.

VBA code to loop and update MS access database column from Excel

Background:
I have an excel spreadsheet that retrieves data from an MS Access database. That code works fine. It retrieves records that have the "comments" field as blank. Users update the comments field in Excel and click a button.
The Ask: Once the button is clicked, the VBA code must loop through all retrieved records in my excel sheet and those records that are marked "completed" in excel must update the same comment in the "comments field" in my database.
I have looked at this article and Gord Thompson posted some code that could work for my situation; except that i dont know how to tailor that code to work for me :(
Link--
VBA code to update / create new record from Excel to Access
**Snapshot of the structure of my database and excel at this ** link
excel:
database:
Will this code work
Sub Update()
Dim cn As ADODB.Connection, rs As ADODB.Recordset
Dim xComments As String
Dim xType As String
Dim xIBES_Ticker As String
Dim xEditor As String
Dim xPRD_Year As String
Dim xPRD_Month As String
Dim xEvent_Date As String
Dim xReporting As String
Dim xNotes As String
' connect to the Access database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; " & _
"Data Source=C:\Database1.mdb;"
' open a recordset
Set rs = New ADODB.Recordset
rs.Open "tablename", cn, adOpenKeyset, adLockOptimistic, adCmdTable
Range("A2").Activate ' row 1 contains column headings
Do While Not IsEmpty(ActiveCell)
'filter all columns and update all records back instead of looking for those marked with "complete"
'guessing this will be easier to do
rs.Filter = "Type='" & xType & "' AND IBES_Ticker='" & xIBES_Ticker & "' AND Editor='" & xEditor & "' AND PRD_Year='" & xPRD_Year & "' AND PRD_Month='" & xPRD_Month & "' AND Event_Date='" & xEvent_Date & "' AND Reporting='" & xReporting & "' AND Notes='" & xNotes & "' AND Comments='" & xComments & "' "
If rs.EOF Then
Debug.Print "No existing records found..."
rs.Filter = ""
Else
Debug.Print "Existing records found..."
End If
rs("Type").Value = xType
rs("IBES_Ticker").Value = xIBES_Ticker
rs("Editor").Value = xEditor
rs("PRD_Year").Value = xPRD_Year
rs("PRD_Month").Value = xPRD_Month
rs("Event_Date").Value = xEvent_Date
rs("Reporting").Value = xReporting
rs("Notes").Value = xNotes
rs("Comments").Value = xComments
rs.Update
Debug.Print "...record update complete."
ActiveCell.Offset(1, 0).Activate ' next cell down
Loop
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
I am not sure what bit of the adaptation you are struggling with. The following might help:
Sub update()
Dim r as Range
Set r = [J2] ' shorthand for Range("J2")
While r.offset(0, -3).Value > 0
If r.Value = "Complete" Then
' take this record and put it in the DB
End If
Set r = r.offset(1,0) ' go to the next row
Wend
End Sub
Is that the bit you had difficulty with? If it is something else, please leave a comment.
UPDATE I don't have Access, so it is a little bit hard to give more guidance. However, I found the following code snippet for updating a record in Access (see http://msdn.microsoft.com/en-us/library/office/ff845201(v=office.15).aspx )
UPDATE tblCustomers
SET Email = 'None'
WHERE [Last Name] = 'Smith'
I think we can use that with the above and do something like this:
Sub update()
Dim cn As ADODB.Connection, rs As ADODB.Recordset
' connect to the Access database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; " & _
"Data Source=C:\Database1.mdb;"
' open a recordset
Set rs = New ADODB.Recordset
rs.Open "tablename", cn, adOpenKeyset, adLockOptimistic, adCmdTable
Dim r as Range
Set r = [J2] ' shorthand for Range("J2")
While r.offset(0, -3).Value > 0
If r.Value = "Complete" Then
ticker = r.offset(0, -7)
notes = r.offset(0, -1)
' create the query string - something like this?
qString = "UPDATE table name SET Notes='" & notes & "' WHERE IBES_Ticker='" & ticker
' now put it in the database:
cn.Execute qString, dbFailOnError
End If
set r = r.offset(1,0) ' go to the next row
Wend
' now close your connections properly…
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub

Recordset count number in VBA

Is there a way to get the number of rows of recordset without using the movelast method in VBA? If I try to use the movelast method, the function will stop as i am using the UDF.
Yes, but you have to open the Recordset using adOpenStatic or adOpenKeyset. Using adOpenStatic will pull the entire Recordset into memory, so it's not a good idea if your application doesn't need to process the entire recordset, you need to view changes made by other users, or if it's too big to fit into memory. On the other hand, processing the Recordset can be much faster because it doesn't have to hit the database for each record.
adOpenStatic example:
' dbConnection is an ADODB.Connection object
rs.Open "source", dbConnection, adOpenStatic
Debug.Print rs.RecordCount
adOpenKeyset example:
rs.Open "source", dbConnection, adOpenKeyset
' note: LockType adLockOptimistic may be required for particular databases
' rs.Open "source", dbConnection, adOpenKeyset, adLockOptimistic
Upon reviewing BzKnt's answer, it appears that another option to gain access to RecordCount is to set the CursorLocation to adUseClient.
Dim rs As New ADODB.Recordset
rs.CursorLocation = adUseClient
rs.Open "source", dbConnection
Also note that this is dependent on the database and driver you are using. Some of these methods may not work for all types of databases.
Article here http://www.geeksengine.com/article/recordcount-ado-recordset-vba.html explains the issue very clearly.
Use VBA to get the correct number of records in a Recordset object
Issue involves the cursor used.
In short add this after setting:
rs = New ADODB.Recordset
' Client-side cursor
rs.CursorLocation = adUseClient
In case anyone is interested in a working example:
'Query a closed excel workbook and assign a column to a array.
Sub TestADO()
Dim MeArr() As Variant
MeArr = ADOLoader ("C:\Closed_Workbook.xlsx", "Sheet1", "Column One")
Debug.Print LBound(MeArr), MeArr(LBound(MeArr))
Debug.Print UBound(MeArr), MeArr(UBound(MeArr))
End Sub
' SubIDCol is the column header
Function ADOLoader(strSourceFile As String, SheetName As String, SubIDCol As String) As Variant
Dim RowPlace, f As Integer
Dim cn As Object, rs As Object, sql As String
Dim ACount As Integer
Dim SubIDArray() As Variant
sql = "Select [" & SubIDCol & "] from [" & SheetName & "$]"
'---Connecting to the Data Source---
Set cn = CreateObject("ADODB.Connection")
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & strSourceFile & ";" & "Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
.CursorLocation = adUseClient
.Open
Set rs = .Execute(sql)
End With
' If you wanted the Headers:
' For f = 0 To rs.Fields.Count - 1
' On Error Resume Next
' .Cells(r, c + f).Formula = rs.Fields(f).Name
' Debug.Print rs.Fields(f).Name
' On Error GoTo 0
' Next f
RowPlace = 0
ACount = rs.RecordCount - 1
ReDim SubIDArray(rs.RecordCount - 1)
On Error Resume Next
rs.MoveFirst
On Error GoTo 0
Do While Not rs.EOF
For f = 0 To rs.Fields.Count - 1
On Error Resume Next
SubIDArray(RowPlace) = rs.Fields(f).Value
RowPlace = RowPlace + 1
On Error GoTo 0
Next f
rs.MoveNext
Loop
'---Clean up---
rs.Close
cn.Close
Set cn = Nothing
Set rs = Nothing
Debug.Print "Lower bound of array = " & LBound(SubIDArray)
Debug.Print "Upper bound of array = " & UBound(SubIDArray)
ADOLoader = SubIDArray()
End Function

Issues connecting to MSSQL through VBA

I'm having some trouble connecting to an MSSQL Server through VBA Below is my code that is having trouble
Set con = New ADODB.Connection
Set rs = New ADODB.Recordset
con.Provider = "sqloledb"
sConnectionString = "Server=SQLServer;Database=DBName;UID=sa;Pwd=NiceTry"
con.Open sConnectionString
'Dim sh As Worksheet
Dim tempSheet As String
tempSheet = "IgnoreMe"
'See if there is already an "IgnoreMe" Sheet, create it if not.
Dim wsSheet As Worksheet
On Error Resume Next
Set wsSheet = Sheets("IgnoreMe")
On Error GoTo 0
If Not wsSheet Is Nothing Then
'Sheet exists, don't recreate it.
Else
Sheets.Add.Name = tempSheet
End If
Set sh = Worksheets("IgnoreMe")
' Clean up the sheet's contents
sh.UsedRange.Clear
' Now get the table's data
rs.Open "SELECT JobHeaderID, Job, ProofApproved, SleeveLabel, MasterLabel" & _
" FROM JobHeader " & _
" WHERE Job IN ('665511', '671259', '671259-1')", con
End Sub
This is just the part to download the information. I have other code to read through the recordset. On the rs.Open line I always get an Automation Error I can't figure out what problem it's hitting. Any ideas on what it's hitting?
I'm trying to follow http://webcheatsheet.com/ASP/database_connection_to_MSSQL.php the piece without DSN
Found a very straightforward example here
Here is my working code sanitized
Sub IterateColE()
' Clean up the destination sheet's contents
Sheets("IgnoreMe").UsedRange.Clear
'We're going to iterate through column E until we hit a blank/empty cell.
For Each currCell In Worksheets("Main").Range("E:E").Cells()
'Oh! and we dont want to get the header row
If currCell.Row 1 Then
If (currCell.Text "") And (currCell.Text vbNullString) Then
'Get values for job in currCell and place in the matching row on IgnoreMe
getValues currCell.Value, currCell.Row
Else
'Well, seems we've hit a blank cell, stop processing
Exit For
End If
End If
Next
End Sub
'Gets the needed values for the job and places them in "IgnoreMe" sheet on specified row. They can then be referenced like "=IgnoreMe!C3"
Sub getValues(job As String, destinationRow As Integer)
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=SQLServer;" & _
"Initial Catalog=InitialTableName;" & _
"UID=DBUsername;Pwd=Nicetry;"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Open the connection and execute.
conn.Open sConnString
Set rs = conn.Execute("SELECT JobHeaderID, Job, DataProofApproved, SleevePackLabel, MasterLabel" & _
" FROM JobHeader " & _
" WHERE Job='" & job & "'")
' Check we have data.
If Not rs.EOF Then
' Transfer result.
Sheets("IgnoreMe").Range("A" & destinationRow).CopyFromRecordset rs
' Close the recordset
rs.Close
Else
MsgBox "Error: No records returned.", vbCritical
End If
' Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
End Sub
'Close out your connection when you close the workbook. Locked database tables are annoying
Private Sub Workbook_Deactivate()
If Not (con Is Nothing) Then
con.Close
Set con = Nothing
End If
End Sub