Previously I was able to run some VBA that ran several SQL queries and put the results in separate columns in a single Excel worksheet. There was a reference set to Microsoft ActiveX Data Objects 2.8 Library (Tools, References in the VBE).
Although the code was working fine, recently I get an error message as follows -
Run-time error '-2147217913 (80040e07)'
Data type mismatch in criteria expression
Here is the code (the error appears in "rs.Open sql, cn, adOpenStatic" which is less than helpful). Please note that the same error appears in all VBA/SQL code I try to run, not just the code below.
Private Sub GetUniqueClassesListWithConditions()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strWkbPath As String
Dim sql As String
Dim buf As Variant
Dim i As Long
Dim j As Long
Dim iTimes As Integer
Dim iQuestion As Integer
Dim iCondition As Integer
Dim iLimit As Integer
Dim sCondition As String
Dim iColumn As Integer
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
strWkbPath = ThisWorkbook.FullName
Worksheets.Add After:=Sheets(Sheets.Count)
For iQuestion = 1 To 14
For iTimes = 1 To 5
Select Case iTimes
Case 1
iLimit = 7
sCondition = "Day"
Case 2
iLimit = 6
sCondition = "Time"
Case 3
iLimit = 16
sCondition = "Faculty"
Case 4
iLimit = 13
sCondition = "Department"
Case 5
iLimit = 6
sCondition = "Student Numbers"
End Select
For iCondition = 1 To iLimit
sql = "SELECT DISTINCT([Data$].Class) FROM [Data$] WHERE [Data$].Q" & iQuestion & " <> '-' AND [Data$]." & sCondition & " = " & iCondition
j = 0
Set cn = New ADODB.Connection
cn.Provider = "Microsoft.ACE.OLEDB.12.0"
cn.Properties("Extended Properties") = "Excel 12.0;HDR=YES;IMEX=1"
cn.Open strWkbPath
Set rs = New ADODB.Recordset
rs.Open sql, cn, adOpenStatic
ReDim buf(0 To rs.Fields.Count - 1, 0)
For i = 0 To rs.Fields.Count - 1
buf(i, 0) = rs(i).Name
Next i
Do Until rs.EOF
j = j + 1
ReDim Preserve buf(0 To rs.Fields.Count - 1, 0 To j)
For i = 0 To rs.Fields.Count - 1
buf(i, j) = rs(i).Value
Next i
rs.MoveNext
Loop
rs.Close
cn.Close
Set cn = Nothing
Set rs = Nothing
iColumn = iColumn + 1
With ActiveSheet
.Cells(1, iColumn).Value = "Q" & iQuestion & ", " & sCondition & "=" & iCondition
.Cells(2, iColumn).Resize(UBound(buf, 2) + 1, UBound(buf, 1) + 1).Value = TransposeArray(buf)
End With
Next iCondition
iColumn = iColumn + 2
Next iTimes
Next iQuestion
With ActiveSheet
.Rows(1).Font.Bold = True
.Rows(2).EntireRow.Delete
.UsedRange.Columns.EntireColumn.AutoFit
On Error Resume Next
.Name = "Unique Classes List (Condtions)"
On Error GoTo 0
End With
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
End Sub
Private Function TransposeArray(buf)
Dim tmp()
Dim i As Long
Dim j As Long
ReDim tmp(UBound(buf, 2), UBound(buf, 1))
For i = LBound(buf, 1) To UBound(buf, 1)
For j = LBound(buf, 2) To UBound(buf, 2)
tmp(j, i) = buf(i, j)
Next j
Next i
TransposeArray = tmp
End Function
If somebody can help me get the code running and/or tell me what the problem is, I'd be very grateful.
Also, I'd like to do the same thing in Access. If I can export all of the results to an Excel file, even as different sheets in a workbook, it's easy for me to run some other code to combine the data into another worksheet. (I'm new to Access and know how to run SQL, but have no idea how to automate it, including putting the results in different "columns" and exporting it)
Should mention my SQL "skills" are basic and I'm learning from w3schools as I go along (hope I'm not offending anybody here.
Thanks for your help in advance.
Related
I’m using ADO to run SQL query in VBA. I’ve done this quite a lot, and everything works properly.
However, I’m advancing to a more sophisticated query, where I need to input an unknown number of conditional strings. In short:
SELECT * FROM database.dbo.table
WHERE Col1 IN (‘val1’, ‘val2’, ..., ‘valn’)
I have a set of data on my worksheet, which changes every time. The data are of the same string format each time, but number of cells with values varies. I want to execute above query, using my n number of variables in the WHERE-statement.
Example of query with 5 variables from worksheet:
SELECT * FROM database.dbo.table
WHERE Col1 IN (‘000165234’, ‘000165238’, ‘000165231’, ‘000165232’, ‘000165239’)
Any pointers to the right direction are greatly appreciated.
My biggest issue is how to handle the unknown number of variables.
Constraints: will always be at least 1 cell with value, and never more than 60.
Notes: Data is also stored in an array, and does not necessarily needs to be printed on the worksheet.
Updated code
Sub TEST()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Set ws1 = ThisWorkbook.Worksheets("Sheet1")
Set ws2 = ThisWorkbook.Worksheets("Sheet2")
Dim fRow As Long
Dim sRow As Integer
Dim col As Integer
Dim arr() As Variant
Dim coll As New Collection
col = 3
sRow = 6
With ws1
fRow = .Cells(.Rows.Count, col).End(xlUp).Row
End With
With ws2
fRow2 = .Cells(.Rows.Count, 12).End(xlUp).Row
End With
For i = sRow To fRow
With ws1
ele1= .Cells(i, 2).Value
ele2= "000" & .Cells(i, 4).Value
If ele1<> "" Then
coll.Add Array(ele2)
End If
End With
Next
On Error GoTo gotcha
ReDim arr(1 To coll.Count, 1 To 2)
For i = 1 To coll.Count
arr(i, 1) = coll(i)(0)
Next
gotcha:
Debug.Print Err.number
If Err.number = 9 Then
MsgBox "Error"
Exit Sub
End If
ws2.Range("L29:M" & fRow2).ClearContents
ws2.Range("L29").Resize(UBound(arr, 1), UBound(arr, 2)).Value = arr
Set conn = CreateObject("ADODB.Connection")
Dim fRow3 As Long
With ws2
fRow3 = .Cells(.Rows.Count, 13).End(xlUp).Row
End With
Dim CONNECTION As String
Dim QUERY As String
Dim WHERE As String
'Set connection and SELECT query
CONNECTION = "Provider=*.1;Persist Security Info=True;User ID=*; Password=*; Data Source=*;Initial Catalog=*"
selectpart = "SELECT *FROM database.dbo.table "
'### The error occurs here ###
conditionpart = "WHERE [COL1] IN ('" & Join(arr, "','") & "')"
GetBreakerQuantitiesQuery = selectpart & vbNewLine & conditionpart
QUERY = GetBreakerQuantitiesQuery
conn.Open CONNECTION
Set rs = CreateObject("ADODB.Recordset")
rs.ActiveConnection = conn
rs.Open QUERY
ws.Range("T6").CopyFromRecordset rs
ws.Range("T6:AL6").Copy
ws.Range("N7").PasteSpecial xlPasteValues, xlPasteSpecialOperationNone, True, True
ws.Range("T6:AL6").ClearContents
ws.Range("L6").Select
rs.Close
conn.Close
Set conn = Nothing
Set rs = Nothing
End Sub
Dim sql as string, arr
arr = Array("000165231", "000165232", "000165239")
sql = "SELECT * FROM database.dbo.table WHERE Col1 IN ('" & Join(arr, "','") & "')"
'use sql variable for your query
Use a 1-d array:
For i = sRow To fRow
With ws1
If Len(.Cells(i, 2).Value) > 0 Then
coll.Add "000" & .Cells(i, 4).Value
End If
End With
Next
On Error GoTo gotcha '??
ReDim arr(0 To coll.Count-1)
For i = 1 To coll.Count
arr(i - 1) = coll(i)
Next
'....
ws2.Range("L29").Resize(UBound(arr) + 1, 1).Value = Application.Transpose(arr)
I am trying to build a validation tool that consists of a header check, a dupe check, and a vLookup. In the DuplicateCheck subroutine, I am adding all unique values from a range to a dictionary using .Exists() = False; this check is failing consistantly and I am getting duplicate values added. Similar problems seemed to be fixed using lower() or upper(), but my testing has been with numbers such as "1", "2", "3", or values such as "k1", "k2", "k2".
Here is my code:
Option Explicit
Dim wbThis As ThisWorkbook
Dim wsOld, wsNew, wsValid As Worksheet
Dim lColOld, lColNew, lRowOld, lRowNew, iRow, iCol As Long
Dim cellTarget, cellKey As Variant
Dim cellValid, dataOld, dataNew As Range
Sub Execute()
Set wbThis = ThisWorkbook
Set wsOld = wbThis.Worksheets(1)
Set wsNew = wbThis.Worksheets(2)
Set wsValid = wbThis.Worksheets(3)
lColOld = wsOld.Cells(1, Columns.Count).End(xlToLeft).Column
lColNew = wsNew.Cells(1, Columns.Count).End(xlToLeft).Column
lRowOld = wsOld.Cells(Rows.Count, 1).End(xlUp).Row
lRowNew = wsNew.Cells(Rows.Count, 1).End(xlUp).Row
Set dataOld = wsOld.Range("A1").Resize(lRowOld, lColOld)
Set dataNew = wsNew.Range("A1").Resize(lRowNew, lColNew)
Call Validation.HeaderCheck
Call Validation.DuplicateCheck
Call Validation.vLookup
End Sub
Sub HeaderCheck()
Application.StatusBar = "Checking headers..."
Dim i As Long
With wsNew
For i = 1 To lColNew
If (wsNew.Cells(1, i) <> wsOld.Cells(1, i)) Then
MsgBox ("Column " & i & " on New Data is not the same as Old Data. This tool will not work with differences in headers. Please reorder your fields and run the tool again.")
Application.StatusBar = False
End
End If
Next i
End With
With wsOld
For i = 1 To lColOld
If (wsOld.Cells(1, i) <> wsNew.Cells(1, i)) Then
MsgBox ("Column " & i & " on Old Data is not the same as New Data. This tool will not work with differences in headers. Please reorder your fields and run the tool again.")
Application.StatusBar = False
End
End If
Next i
End With
Application.StatusBar = False
End Sub
Sub DuplicateCheck()
Dim iterator As Long
Dim dicKeys As New Scripting.Dictionary
Dim dicDupes As New Scripting.Dictionary
Dim key As Variant
Dim progPercent As Double
Dim keys As Range
Dim wsDupes As Worksheet
Set keys = wsNew.Range("A2").Resize(lRowNew, 1)
Application.ScreenUpdating = False
iterator = 1
For Each key In keys
If dicKeys.Exists(key) = False Then
dicKeys.Add key, iterator 'HERE IS THE BUG----------------------
Else
dicDupes.Add key, iterator
End If
progPercent = iterator / keys.Count
Application.StatusBar = "Identifying duplicates: " & Format(progPercent, "0%")
iterator = iterator + 1
Next key
If (dicDupes.Count <> 0) Then
Set wsDupes = ThisWorkbook.Worksheets.Add(, wsValid, 1)
wsDupes.Name = "Duplicates"
iterator = 1
For Each key In dicDupes
If (dicDupes(key) <> "") Then
wsDupes.Cells(iterator, 1).Value = dicDupes(key)
End If
progPercent = iterator / dicDupes.Count
Application.StatusBar = "Marking duplicates: " & Format(progPercent, "0%")
iterator = iterator + 1
Next key
End If
Set dicKeys = Nothing
Set dicDupes = Nothing
Application.ScreenUpdating = True
End Sub
Sub vLookup()
Application.ScreenUpdating = False
Dim progPercent As Double
For iRow = 2 To lRowNew
Set cellKey = wsNew.Cells(iRow, 1)
For iCol = 1 To lColNew
Set cellTarget = wsNew.Cells(iRow, iCol)
Set cellValid = wsValid.Cells(iRow, iCol)
On Error GoTo errhandler
If (IsError(Application.vLookup(cellKey.Value, dataOld, iCol, False)) = False) Then
If (cellTarget = Application.vLookup(cellKey.Value, dataOld, iCol, False)) Then
cellValid.Value = cellTarget
Else
cellValid.Value = "ERROR"
End If
Else
If (cellValid.Column = 1) Then
If (cellValid.Column = 1) Then
cellValid.Value = cellKey
cellValid.Interior.ColorIndex = 46
End If
Else
cellValid.Value = "ERROR"
End If
End If
Next iCol
progPercent = (iRow - 1) / (lRowNew - 1)
Application.StatusBar = "Progress: " & iRow - 1 & " of " & lRowNew - 1 & ": " & Format(progPercent, "0%")
Next iRow
Application.StatusBar = False
Application.ScreenUpdating = True
Exit Sub
errhandler:
MsgBox (Err.Description)
End Sub
The problem is probably here:
Dim key As Variant
Dim progPercent As Double
Dim keys As Range
Then when you make the check here:
For Each key In keys
If dicKeys.Exists(key) = False Then
dicKeys.Add key, iterator 'HERE IS THE BUG----------------------
Else
dicDupes.Add key, iterator
End If
Next
It compares the key as Range and not as value.
Try something like this:
If dicKeys.Exists(key.Value2) = False Then
dicKeys.Add key.Value2, iterator
Or find another way not to work with the object, but with its value.
I'm trying to load a table from Microsoft Access and paste it into Excel cells with VBA.
My path is correctly finding my .accdb file and does error until the first Cells(row x).Value = statement.
The "OpenRecordset" method not referencing a table, makes me feel like I shouldn't be passing in the name of the table- or using a different method altogether.
I get an error: "Run-time error '3265' Application-defined or object-defined error
Here is my code below:
Sub ImportAccessButton()
Dim row As Integer
Dim dbPassengerCarMileage As Database
Dim rstPassengerCarMileage As Recordset
row = 3
Set dbPassengerCarMileage = OpenDatabase(ThisWorkbook.Path & "\Cars.accdb")
Set rstPassengerCarMileage = dbPassengerCarMileage.OpenRecordset("Amber")
If Not rstPassengerCarMileage.BOF Then
Do Until rstPassengerCarMileage.EOF
Cells(row, 1).Value = rstPassengerCarMileage!MAKE
Cells(row, 2).Value = rstPassengerCarMileage!Model
Cells(row, 3).Value = rstPassengerCarMileage!VOL
Cells(row, 4).Value = rstPassengerCarMileage!HP
Cells(row, 5).Value = rstPassengerCarMileage!MPG
Cells(row, 6).Value = rstPassengerCarMileage!SP
Cells(row, 7).Value = rstPassengerCarMileage!WT
row = row + 1
rstPassengerCarMileage.MoveNext
Loop
End If
'Close database and Cleanup objects
rstPassengerCarMileage.Close
dbPassengerCarMileage.Close
Set rstPassengerCarMileage = Nothing
Set dbPassengerCarMileage = Nothing
End Sub
It uses ADODB. The CopyFromRecordset command speeds up.
Sub ImportAccessButton()
Dim Rs As Object
Dim strConn As String
Dim i As Integer
Dim Ws As Worksheet
Dim strSQL As String
set Ws = ActiveSheet
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.Path & "\Cars.accdb" & ";"
strSQL = "SELECT * FROM Amber"
Set Rs = CreateObject("ADODB.Recordset")
Rs.Open strSQL, strConn
If Not Rs.EOF Then
With Ws
.Range("a2").CurrentRegion.ClearContents
For i = 0 To Rs.Fields.Count - 1
.Cells(2, i + 1).Value = Rs.Fields(i).Name
Next
.Range("a" & 3).CopyFromRecordset Rs
End With
End If
Rs.Close
Set Rs = Nothing
End Sub
I have a slight problem in Excel. I need to sync up the values in the curly braces {} found in column C and put them against the user id in column F. I would like this data to be copied across to a new worksheet in the same workbook. Any ideas how I could accomplish this? You don't have to provide any code but a nudge in the right direction would be great.
E.g. on the Emails sheet
becomes this on a new sheet
In case anyone needs help, this is the solution:
Sub CopyConditional()
Dim wshS As Worksheet
Dim WhichName As String
Set wshS = ActiveWorkbook.Sheets("Emails")
WhichName = "NewSheet"
Const NameCol = "C"
Const FirstRow = 1
Dim LastRow As Long
Dim SrcRow As Long
Dim TrgRow As Long
Dim wshT As Worksheet
Dim cpt As String
Dim user As String
Dim computers() As String
Dim computer As String
On Error Resume Next
Set wshT = Worksheets(WhichName)
If wshT Is Nothing Then
Set wshT = Worksheets.Add(After:=wshS)
wshT.Name = WhichName
End If
On Error GoTo 0
If wshT.Cells(1, NameCol).value = "" Then
TrgRow = 1
Else
TrgRow = wshT.Cells(wshT.Rows.Count, NameCol).End(xlUp).Row + 1
End If
LastRow = wshS.Cells(wshS.Rows.Count, NameCol).End(xlUp).Row
For SrcRow = FirstRow To LastRow
cpt = wshS.Range("C" & SrcRow).value
user = wshS.Range("F" & SrcRow).value
If InStr(cpt, ":") Then
cpt = Mid(cpt, InStr(1, cpt, ":") + 1, Len(cpt))
End If
If InStr(cpt, ";") Then
computers = Split(cpt, ";")
For i = 0 To UBound(computers)
If computers(i) <> "" Then
wshT.Range("A" & TrgRow).value = user
wshT.Range("B" & TrgRow).value = Mid(Left(computers(i), Len(computers(i)) - 1), 2)
TrgRow = TrgRow + 1
End If
Next
Else
computer = cpt
If computer <> "" Then
wshT.Range("A" & TrgRow).value = user
wshT.Range("B" & TrgRow).value = Mid(Left(computer, Len(computer) - 1), 2)
TrgRow = TrgRow + 1
End If
End If
Next SrcRow
End Sub
You didn't ask a question. Basically what you would do is
loop through the values in column F
for each value, get the value in column C
loop through all braced values in column C
let braceValue = parse column C searching for {value}
create a row in new worksheet with column F value, braceValue
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have 9 sheets that connect to different tables in teradata, each time i have to enter my user name and password to refresh and get new set of data. could someone please advice how do i write a VBA code that could change the connection string for each connection and refresh the data table.I am a begginner in VBA and have no clue in codding in VBA
Thanks
Syam
Here is what I do: I put the following in cells A2:B5
Data Source:
Database:
I put the SQL in cell D2. I use Row 1 for telling me how long the query takes. Then, I add a button anywhere on the page. Then I call the code below. It looks complicated, but the core of the functionality is all in Get_Data_Teradata.
The Get_SQL Function simply reads down column D until it finds a blank row and then returns a big block of text for the SQL. You could replace this with a hardcoded SQL statement.
Pop_Col_Heads puts the column headings from the result in Row 1. Note, that I have discovered a Bug in Excel 2010 on Win 7 where I can only populate columns once or twice per Excel session. If I quit and load Excel again, it works another once or twice.
Copy_Data_From_RDBMS places the ADODB RecordSet into a range in the active sheet. I had to do some tweaks to handle inserts and updates because they don't return any rows.
Sub Get_Data_Teradata()
'Supports Multi Query
Dim cn As ADODB.Connection
Dim sConnect As String
Set cn = New ADODB.Connection
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
Dim cmdSQLData As ADODB.Command
Set cmdSQLData = New ADODB.Command
Dim sQueries() As String
sConnect = "Persist Security Info=True; Session Mode=ANSI; " & _
"Data Source=" & ActiveSheet.Range("B2").Value & ";" & _
"Database=" & ActiveSheet.Range("B3").Value & ";" & _
"User ID=" & ActiveSheet.Range("B4").Value & ";" & _
"Password=" & ActiveSheet.Range("B5").Value & ";"
sQueries = Get_SQL(ActiveSheet.Range("D2:D9999"))
nRow = 1 'initialize to start at the top of the page
For i = 0 To UBound(sQueries) - 1
cn.Open sConnect
Set cmdSQLData.ActiveConnection = cn
cmdSQLData.CommandText = sQueries(i) 'TELL VBA TO LOAD THE QUERY INTO TERADATA
cmdSQLData.CommandType = adCmdText
cmdSQLData.CommandTimeout = 0
Set rs = cmdSQLData.Execute()
Call Pop_Col_Heads(rs, nRow)
nRow = Copy_Data_From_RDBMS(rs, nRow)
cn.Close
Next i
End Sub
Dim a As Long
Dim i As Long
Dim nIndex As Long
Dim sSQL() As String
Function Get_SQL(oRange As Object) As String()
'First figure out how many rows the SQL statement is
a = 0
For Each cCell In oRange
a = a + 1
If cCell.Value = "" Then
a = a - 1
Exit For
End If
Next cCell
'Num rows = a now
'Step through and parse into array
i = 0
nIndex = 0
ReDim Preserve sSQL(1)
For Each cCell In oRange
i = i + 1
If i > a Then
Exit For
ElseIf cCell.Value = "<Multi>" Then
nIndex = nIndex + 1
ReDim Preserve sSQL(nIndex + 1)
Else
sSQL(nIndex) = sSQL(nIndex) & To_Text(cCell.Value) & " "
End If
Next cCell
Get_SQL = sSQL
End Function
Sub Pop_Col_Heads(rs As Object, nRow As Long)
Dim rHeads As Range
Dim fFields As Field
Dim nCol As Integer
nCol = 0
If nRow = 1 Then
ActiveSheet.Range("E1:ZZ1").ClearContents
End If
Set rHeads = ActiveSheet.Range("E1").Offset(nRow - 1, 0)
Do While nCol < rs.Fields.Count
sTemp = rs.Fields(nCol).Name
rHeads.Cells(nRow, nCol + 1).Value = rs.Fields(nCol).Name
ActiveSheet.Calculate
rHeads.Cells(nRow, nCol + 1).Value = sTemp
nCol = nCol + 1
rHeads.WrapText = True
rHeads.VerticalAlignment = xlVAlignTop
Loop
End Sub
Function Copy_Data_From_RDBMS(rs As Object, nRow As Long) As Long
'Supports Multi Query
If nRow = 1 Then
x = Get_Last_Row_Find(ActiveSheet.Range("E1:ZZ64000"))
ActiveSheet.Range("E2:ZZ" & x).ClearContents
End If
On Error Resume Next
rs.MoveFirst
On Error GoTo 0
If Not rs.EOF Then
ActiveSheet.Range("E2").Offset(nRow - 1, 0).CopyFromRecordset rs
x = Get_Last_Row_Find(ActiveSheet.Range("E1:ZZ64000"))
Copy_Data_From_RDBMS = x + 1
ActiveSheet.Range("E2:ZZ" & x).Offset(nRow - 1, 0).WrapText = False
Else 'no results (e.g. insert)
ActiveSheet.Range("E2").Offset(nRow - 1, 0).Value = "<no data returned>"
End If
rs.Close
Set rs = Nothing
End Function