I am trying to write a VBA code that Queries some values.
My SQL query has two conditions in the WHERE statement.
The value in column B is within the query, so no issue about it.
The value in column A is a code which starts with numbers, but contains Letters and should be seen as a string as such to work '6F3S'; specifically: database.columnA='6F3S'.
My issue comes from the fact that I want to outsource the element 6F3S in cell D1 in sheet "Input_sheet", so that the user can change it with other codes if necessary.
This is my code so far:
Sub Query1()
Dim ValueCellD1 As String
ValueCellD1 = Worksheets("Input_Sheet").Range("D1").Value
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
"FFFF;DSN=XXXXXXXXXXXX;", Destination:=Range("$A$7")).QueryTable
.CommandText = Array( _
"SELECT database.columnA, database.columnB, database.columnC" _
& Chr(13) & "" & Chr(10) & _
"FROM IMPALA.database database" _
& Chr(13) & "" & Chr(10) & _
"WHERE (database.columnA=ValueCellD1) AND (database.columnB='London')")
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = True
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.AdjustColumnWidth = True
.RefreshPeriod = 0
.PreserveColumnInfo = True
.ListObject.DisplayName = "YYYYYYYYYYYYYYYYY"
.Refresh BackgroundQuery:=False
End With
End Sub
I receive a
Run-time error '13': Type mismatch
When I try to debug it appears being here:
.CommandText = Array( _
"SELECT database.columnA, database.columnB, database.columnC" _
& Chr(13) & "" & Chr(10) & _
"FROM IMPALA.database database" _
& Chr(13) & "" & Chr(10) & _
"WHERE (database.columnA=ValueCellD1) AND (database.columnB='London')")
Note: I am currently using Excel 2010
You need to get ValueCellD1 out of the string so it is recognized as variable.
.CommandText = Array( _
"SELECT database.columnA, database.columnB, database.columnC" _
& Chr(13) & "" & Chr(10) & _
"FROM IMPALA.database database" _
& Chr(13) & "" & Chr(10) & _
"WHERE (database.columnA='" & ValueCellD1 & "') AND (database.columnB='London')")
Actually I have no idea why you insert & Chr(13) & "" & Chr(10) & in your string. The following should work too:
.CommandText = Array( _
"SELECT database.columnA, database.columnB, database.columnC " _
"FROM IMPALA.database database " _
"WHERE (database.columnA='" & ValueCellD1 & "') AND (database.columnB='London')")
Note that if you read a cell value into your variable
ValueCellD1 = Worksheets("Input_Sheet").Range("D1").Value
and push this into your SQL command without verifying the cell value, then anyone who can edit the cell value in D1 can easily attack your database and run any SQL command he likes (eg delete it). Never trust user input. Always validate it.
See https://en.wikipedia.org/wiki/SQL_injection.
Related
I have been test the following macro. It originates from a recorded macro by running QueryTable, and is intended to get data table for an appointed stock from a site.
Some information about the site for one stock is as follows.
stock code & structure of the tables
I modified the original macro, making the stock code, the table number and table date into arguments, so that a loop could be invoked to get tables with the assigned "table number/table date" for a whole list of stock codes.
Following is the modified macro.
Sub Fetch_Table(code As String, tableDate As String)
Dim sourceFullName As String
sourceFullName = "http://emweb.securities.eastmoney.com/PC_HSF10/BusinessAnalysis/Index?type=web&code=" & code
ActiveWorkbook.Queries.Add Name:="Table 0", Formula:= _
"let" & Chr(13) & "" & Chr(10) & " Source = Web.Page(Web.Contents(""" & sourceFullName & """))," & Chr(13) & "" & Chr(10) & " Data0 = Source{0}[Data]," & Chr(13) & "" & Chr(10) & " #""Changed Type"" = Table.TransformColumnTypes(Data0,{{""" & tableDate & """, type text},{""主营构成"", type text}, {""主营收入(元)"", type text}, {""收入比例"", Percentage.Type}, {""主营成本(元)"", type text}, {""成本比" & _
"例"", type text}, {""主营利润(元)"", type text}, {""利润比例"", type text}, {""毛利率(%)"", type text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & " #""Changed Type"""
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
"OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=""Table 0"";Extended Properties=""""" _
, Destination:=Range("$A$5")).QueryTable
.CommandType = xlCmdSql
.CommandText = Array("SELECT * FROM [Table 0]")
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.PreserveColumnInfo = True
.ListObject.DisplayName = "Table_0"
.Refresh BackgroundQuery:=False
End With
End Sub
The code As String, tableDate as String work well. And I call Fetch_Table(code,tableDate), with this two parameters passed.
Then I tried to turn the number in Source{0}[Data] in to a reference, which means I can choose which talbe to fetch, and make the number match the date of the table. So I tested the following code, which treated N as a local variable temporarily, before making it into a parameter. All the rest are same as the above well working macro.
Sub Fetch_Table(code As String, tableDate As String)
Dim sourceFullName As String
sourceFullName = "http://emweb.securities.eastmoney.com/PC_HSF10/BusinessAnalysis/Index?type=web&code=" & code
N = 0
ActiveWorkbook.Queries.Add Name:="Table 0", Formula:= _
"let" & Chr(13) & "" & Chr(10) & " Source = Web.Page(Web.Contents(""" & sourceFullName & """))," & Chr(13) & "" & Chr(10) & " Data0 = Source{N}[Data]," & Chr(13) & "" & Chr(10) & " #""Changed Type"" = Table.TransformColumnTypes(Data0,{{""" & tableDate & """, type text},{""主营构成"", type text}, {""主营收入(元)"", type text}, {""收入比例"", Percentage.Type}, {""主营成本(元)"", type text}, {""成本比" & _
"例"", type text}, {""主营利润(元)"", type text}, {""利润比例"", type text}, {""毛利率(%)"", type text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & " #""Changed Type"""
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
"OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=""Table 0"";Extended Properties=""""" _
, Destination:=Range("$A$5")).QueryTable
.CommandType = xlCmdSql
.CommandText = Array("SELECT * FROM [Table 0]")
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.PreserveColumnInfo = True
.ListObject.DisplayName = "Table_0"
.Refresh BackgroundQuery:=False
End With
End Sub
However, when I called this macro again, an error msgbox came out.
enter image description here
At first, I tried to explicited define the type of N as integer, and then I tried to change the type of N into string. But they all failed.
I assumed that the nubmer in Source{0}[Data] could be taken palce by a variable, and the prolem stemmed from syntax mistakes.
I don't know whether my assumption is right.
I have the following code in an Excel file:
Sub PullData()
ActiveWorkbook.Queries.Add Name:="Sheet1", Formula:= _
"let" & Chr(13) & "" & Chr(10) & " Source = Excel.Workbook(Web.Contents(""Link.xlsx""), null, true)," & Chr(13) & "" & Chr(10) & " Sheet1_Sheet = Source{[Item=""Sheet1"",Kind=""Sheet""]}[Data]," & Chr(13) & "" & Chr(10) & " #""Changed Type"" = Table.TransformColumnTypes(Sheet1_Sheet,{{""Column1"", type text}, {""Column2"", type text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & "" & _
" #""Changed Type"""
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
"OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=Sheet1;Extended Properties=""""" _
, Destination:=Range("$B$3")).QueryTable
.CommandType = xlCmdSql
.CommandText = Array("SELECT * FROM [Sheet1]")
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.PreserveColumnInfo = True
.ListObject.DisplayName = "Sheet1"
.Refresh BackgroundQuery:=False
End With End Sub
Sub ClearData() ClearData Macro
Columns("B:C").Select
Selection.ClearContents
ActiveWorkbook.Queries("Sheet1").Delete
End Sub
On my computer (and several other computers from my company) this file works without any problems. However, on some computers, I receive
"Run-time error: '438': Object doesn't support this property or
method"
in VBA, it highlights the row ActiveWorkbook.Queries("Sheet1").Delete.
To explain a bit how it should work, I have an Online Excel file that contains a list with entries. There are 2 buttons, first that pulls data from that file, second one that deletes the data. I would have used only one button, that would refresh the database, but, for some reason, I keep receiving an error related to a query that already exists.
I apologize if this was asked before, but I am just begining to work with VBA and any support is more than welcomed.
Apparently Workbook objects do not have a Queries collection. Did you maybe mean ActiveWorkbook.Worksheets("Sheet1").Delete?
I will begin by clearly stating that I am not a programmer, I am an accountant at heart!
I have a need to return into excel all transactions relating to jobs that have had transactions in a given week (i.e. so that I can see an in week amount and total to date amount).
I'm reasonably proficient with VBA in excel (as accountants go anyway!) but I have always just copied and bodged the same old SQL statement. Essentially, what I think I need to do is a sub query in place of the order number of the WHERE statement in the following:
With Sheet1.QueryTables.Add(Connection:=Array(Array( _
"ODBC;DRIVER={iSeries Access ODBC Driver};SYSTEM=JADE;DBQ=QGPL LIVDTALIB;DFTPKGLIB=QGPL;LANGUAGEID=ENU;PKG=QGPL/DEFAULT(IBM),2,0,1,0,"), _
Array("512;QRYSTGLMT=-1;")), Destination:=Sheet1.Range("A1"))
.CommandText = Array( _
"SELECT SLBGDTPF.BGMCU, SLBGDTPF.BGDSDT, SLBGDTPF.ORTYPE, SLBGDTPF.ORDNO, SLBGDTPF.BGDSVL, SLBGDTPF.BGCD, ", _
"SLBGDTPF.ADBBG, SLBGDTPF.BGRMK" _
& Chr(13) & "" & Chr(10) & _
"FROM RCHASE5C.LIVDTALIB.SLBGDTPF SLBGDTPF" _
& Chr(13) & "" & Chr(10) & _
"WHERE (SLBGDTPF.ORDNO='30214884')")
.Name = "TEST Query"
.FieldNames = True
.RefreshStyle = xlOverwriteCells
.Refresh BackgroundQuery:=False
End With
As a standalone query, what the sub-query element looks like is as follows:
With Sheet2.QueryTables.Add(Connection:=Array(Array( _
"ODBC;DRIVER={iSeries Access ODBC Driver};SYSTEM=JADE;DBQ=QGPL LIVDTALIB;DFTPKGLIB=QGPL;LANGUAGEID=ENU;PKG=QGPL/DEFAULT(IBM),2,0,1,0,"), _
Array("512;QRYSTGLMT=-1;")), Destination:=Sheet2.Range("A1"))
.CommandText = Array( _
"SELECT SLBGDTPF.ORDNO" _
& Chr(13) & "" & Chr(10) & _
"FROM RCHASE5C.LIVDTALIB.SLBGDTPF SLBGDTPF" _
& Chr(13) & "" & Chr(10) & _
"WHERE SLBGDTPF.BGPSDT='20180420'" _
& Chr(13) & "" & Chr(10) & _
"GROUP BY SLBGDTPF.ORDNO")
.Name = "TEST Query"
.FieldNames = True
.RefreshStyle = xlOverwriteCells
.Refresh BackgroundQuery:=False
End With
I'm open to all suggestions, including alternate approaches (I did try using IN and referencing a range in a sheet but I need to pass up to 1,000 different job numbers)
Just as an FYI, this is a template that will be sent out to people within the business to update themselves hence the need to build the connection, rather than just have them refresh an existing query(s) within the workbook.
All the best,
Joe
This can be achieved a few ways, but presuming the database can handle subqueries, I would try a WHERE IN term. I've also made some other cursory edits for clarity. The ultimate GROUP BY term is redundant in the subquery as there is only a single subquery SELECT field and no aggregation going on.
With Sheet1.QueryTables.Add(Connection:=Array(Array( _
"ODBC;DRIVER={iSeries Access ODBC Driver};SYSTEM=JADE;DBQ=QGPL LIVDTALIB;DFTPKGLIB=QGPL;LANGUAGEID=ENU;PKG=QGPL/DEFAULT(IBM),2,0,1,0,"), _
Array("512;QRYSTGLMT=-1;")), Destination:=Sheet1.Range("A1"))
.CommandText = Array( _
"SELECT SLBGDTPF.BGMCU, SLBGDTPF.BGDSDT, SLBGDTPF.ORTYPE, SLBGDTPF.ORDNO, SLBGDTPF.BGDSVL, SLBGDTPF.BGCD, SLBGDTPF.ADBBG, SLBGDTPF.BGRMK" & vbCrLf & _
"FROM RCHASE5C.LIVDTALIB.SLBGDTPF SLBGDTPF" & vbCrLf & _
"WHERE SLBGDTPF.ORDNO IN (" & vbCrLf & _
"SELECT SLBGDTPF.ORDNO" & vbCrLf & _
"FROM RCHASE5C.LIVDTALIB.SLBGDTPF SLBGDTPF" & vbCrLf & _
"WHERE SLBGDTPF.BGPSDT='20180420')")
.Name = "TEST Query"
.FieldNames = True
.RefreshStyle = xlOverwriteCells
.Refresh BackgroundQuery:=False
End With
As #IanPeters already mentioned, there are a few ways to handle this in SQL. I would like to add two versions that use a join instead of one of the WHERE clauses.
You might want to test which version performs best on your database. This will depend on the index structure on the database and on how well the query optimizer handles the query.
Subquery in join:
With Sheet1.QueryTables.Add(Connection:=Array(Array( _
"ODBC;DRIVER={iSeries Access ODBC Driver};SYSTEM=JADE;DBQ=QGPL LIVDTALIB;DFTPKGLIB=QGPL;LANGUAGEID=ENU;PKG=QGPL/DEFAULT(IBM),2,0,1,0,"), _
Array("512;QRYSTGLMT=-1;")), Destination:=Sheet1.Range("A1"))
.CommandText = Array( _
"SELECT SLBGDTPF.BGMCU, SLBGDTPF.BGDSDT, SLBGDTPF.ORTYPE, SLBGDTPF.ORDNO, SLBGDTPF.BGDSVL, SLBGDTPF.BGCD, SLBGDTPF.ADBBG, SLBGDTPF.BGRMK" & vbCrLf & _
"FROM RCHASE5C.LIVDTALIB.SLBGDTPF SLBGDTPF" & vbCrLf & _
"INNER JOIN" & vbCrLf & _
"(SELECT S.ORDNO" & vbCrLf & _
"FROM RCHASE5C.LIVDTALIB.SLBGDTPF S" & vbCrLf & _
"WHERE WeekOrders.BGPSDT='20180420') WeekOrders" & vbCrLf & _
"ON SLBGDTPF.ORDNO = WeekOrders.ORDNO")
.Name = "TEST Query"
.FieldNames = True
.RefreshStyle = xlOverwriteCells
.Refresh BackgroundQuery:=False
End With
Condition outside of join:
With Sheet1.QueryTables.Add(Connection:=Array(Array( _
"ODBC;DRIVER={iSeries Access ODBC Driver};SYSTEM=JADE;DBQ=QGPL LIVDTALIB;DFTPKGLIB=QGPL;LANGUAGEID=ENU;PKG=QGPL/DEFAULT(IBM),2,0,1,0,"), _
Array("512;QRYSTGLMT=-1;")), Destination:=Sheet1.Range("A1"))
.CommandText = Array( _
"SELECT SLBGDTPF.BGMCU, SLBGDTPF.BGDSDT, SLBGDTPF.ORTYPE, SLBGDTPF.ORDNO, SLBGDTPF.BGDSVL, SLBGDTPF.BGCD, SLBGDTPF.ADBBG, SLBGDTPF.BGRMK" & vbCrLf & _
"FROM RCHASE5C.LIVDTALIB.SLBGDTPF SLBGDTPF" & vbCrLf & _
"INNER JOIN RCHASE5C.LIVDTALIB.SLBGDTPF WeekOrders" & vbCrLf & _
"ON SLBGDTPF.ORDNO = WeekOrders.ORDNO" & vbCrLf & _
"WHERE WeekOrders.BGPSDT='20180420'")
.Name = "TEST Query"
.FieldNames = True
.RefreshStyle = xlOverwriteCells
.Refresh BackgroundQuery:=False
End With
I'm recording a macro to automate the process of gathering P/E ratio information from a website for various stock tickers. The macro involves two web queries. I recorded them both the exact same way, and they both use the same URL. One of these queries works fine, and the other gives an
error 1004 "Application-defined or object-defined error"
I have pasted the code for that query below.
ActiveWorkbook.Queries.Add Name:="MSFT PE Ratio (TTM) Range, Past 5 Years", _
Formula:= _
"let" & Chr(13) & "" & Chr(10) & "
Source = Web.Page(Web.Contents(""https://ycharts.com/companies/" & ActiveCell.Value & "/pe_ratio""))," & Chr(13) & "" & Chr(10) & " Data3 = Source{3}[Data]," & Chr(13) & "" & Chr(10) & " #""Changed Type"" = Table.TransformColumnTypes(Data3,{{""Column1"", type text}, {""Column2"", type number}, {""Column3"", type date}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & " #""Changed Type"""
ActiveWorkbook.Worksheets.Add
ActiveSheet.Name = "query2"
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:=Array( _
"OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=""" & ActiveCell.Value & "PE Ratio (TTM) Range, Past 5 Years"";Extended Properties=" _
, """"""), Destination:=Range("$A$1")).QueryTable
.CommandType = xlCmdSql
.CommandText = Array( _
"SELECT * FROM [" & ActiveCell.Value & "PE Ratio (TTM) Range, Past 5 Years]")
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.PreserveColumnInfo = True
.ListObject.DisplayName = "_PE_Ratio__TTM__Range__Past_5_Years"
**.Refresh BackgroundQuery:=False** <-- this is where I get the error
End With
This is code for one web query and one database query. Not two webqueries.
Both querytypes can make use of parameters which you can attach to a cell and set to automatically refresh the querytable when that cell changes.
See: Webquery with parameters and http://dailydoseofexcel.com/archives/2004/12/13/parameters-in-excel-external-data-queries/
When I run the following code I receive an error, "Run-time error '1004': Application-defined or object-defined error"
When I select debug, the following line is highlighted: .Refresh BackgroundQuery:=False
querystr = "SET NOCOUNT ON" & Chr(13) & _
"SELECT CSD.StoreNo AS 'StoreNo',SUM(CSD.Amount) as totalSales " & Chr(13) & _
"INTO #SalesOfTheStores " & Chr(13) & _
"FROM Purchase.dbo.CashsheetDetail as CSD " & Chr(13) & _
"INNER JOIN Purchase.dbo.CashsheetHeader as CSH on CSH.TransferID = CSD.TransferID and CSH.StoreNo = CSD.StoreNo " & Chr(13) & _
"WHERE CSD.Comments = 'Total Gross Sales' AND CSH.DayendDate between '" & StartDate & "' And '" & EndDate & "' " & Chr(13) & _
"GROUP BY CSD.StoreNo; "
With ActiveSheet.QueryTables.Add(Connection:= _
"ODBC;DRIVER=SQL Server;SERVER=" & Div & "DBL01\SR;UID=" & User & ";APP=Microsoft Office 2003;WSID=" & PC & ";DATABASE=Purchase;Trusted_Connection=Yes" _
, Destination:=Range("A1"))
.RefreshStyle = xlOverwriteCells
.CommandText = querystr
.Refresh BackgroundQuery:=False
End With
Your query string does not return data. Change your SQL to be a SELECT statement rather than a SELECT INTO (remove the INTO clause).