VBA sql query based off combobox - sql-server

I've been searching hard the past few days and have come across numerous examples that outline what I'm trying to do. However, I can't seem to get this working. I have a combobox that populates data (a company name) from a table when the form initializes. I then want to take the value chosen in the combo box and run another query to cross reference an id number in that same table.
Private Sub CommandButton1_Click()
Dim myCn As MyServer
Set myCn = New MyServer
Dim rs As ADODB.recordset
Set rs = New ADODB.recordset
Dim sqlStr As String
Dim CompField As String
'CompField = ComboBox1.Value
sqlStr = "SELECT DISTINCT [acctno] FROM client WHERE [company] = '" & ComboBox1.Text & "'"
'sqlStr = "Select DISTINCT [company] FROM client;"
' sqlStr = "SELECT DISTINCT [acctno] FROM client WHERE [company] = " & UserForm1.ComboBox1.Value & ";"
'sqlStr = "SELECT DISTINCT [acctno] FROM client WHERE [company] = " & UserForm1.ComboBox1.Text & ";"
'sqlStr = "SELECT DISTINCT [acctno] FROM client WHERE [company] = 'Company XYZ';"
'sqlStr = "SELECT DISTINCT [acctno] FROM client WHERE company = " & CompField & ""
rs.Open sqlStr, myCn.GetConnection, adLockOptimistic, adCmdText
MsgBox sqlStr
'MsgBox ComboBox1.Value
'MsgBox rs(0)
rs.Close
myCn.Shutdown
Set rs = Nothing
Set myCn = Nothing
End Sub
Currently with the combobox value encased in single quotes i get the entire sql string returned. If I remove the single quotes I get a syntax error referencing part of the combobox value. All other efforts have resulted in run-time errors that have led me nowhere.
I know my query works because I've tested it in SQL Studio and if I hard code a text value in this code I also get the Account ID I'm looking for. Not sure what I'm missing here.

Well I figured it out. I was expecting to be able to MsgBox the variable assigned to the SQL query and see my results. But it doesn't work that way. I had to use ADO GetString method for that and assign another variable. Oh and you were right about escaping, I had to add delimters to properly handle the ComboBox value in the query which ultimately looked like this
sqlStr = "SELECT DISTINCT [acct_no] FROM client WHERE [company] = " & Chr(39) & Me.ComboBox1.Value & Chr(39)
Thanks for your help on this

Related

Excel VBA: ODBC SQL server driver query timeout expired

I have the below VBA query used in Excel 2016 that exacutes a MS Sql stored procedure, sometimes it executes smoothly and returns the recordset, but more often I get an error [Microsoft][ODBC SQL Server Driver] query timeout expired.
At the same time when we go to SSMS and execute the query it runs without issues.
This assumes the issue is rather caused by Excel/VB than by SQL or the query itself.
Searching for this error results in checking network firewalls, but we tried on other machines without firewalls, problems persists.
Here is the VB code:
Public Sub GetDataset2()
Dim cn As ADODB.Connection
Dim cm As Object
Dim rs As ADODB.Recordset
Dim UID, PWD, DB As String
UID = "userId"
PWD = "passworD"
DB = "192.168.1.1"
Set cn = New ADODB.Connection
Set cm = CreateObject("ADODB.Command")
cm.CommandTimeout = 0
cn.Open ("Driver={SQL Server};Server=" & DB & ";Database=myDatabaseName;Trusted_Connection=no;Timeout=900;Uid=" & UID & ";Pwd=" & PWD)
Set rs = cn.Execute("Get_dataset2 '" & Format(Range("dateFrom"), "yyyy-mm-dd") & "' ,'" & Format(Range("dateTo"), "yyyy-mm-dd") & "' ")
Dim lRow As Long
'Find the last non-blank cell in column A(1)
lRow = Sheets("data").Cells(Rows.Count, 1).End(xlUp).Row
lr = "A" & lRow + 1
Sheets("data").Range(lr).CopyFromRecordset rs 'insert data
cn.Close
End Sub
Any suggestion is appreciated.
Joel
After some more thought about the question and the comments on my prior answer, here are some additional points. To BitAccesser, cn.CommandTimeout is the same as Connection.CommandTimeout since the originally submitted code had already dimensioned and set the cn object as an ADODB.Connection. Also worth noting is the difference between ConnectionTimeout and CommandTimeout. The connection timeout is network level, while the command timeout is SQL Server level. In this case, even though a ADODB.Command object is instantiated, it isn't used. Another point relates to the connection string. The connection timeout could be referenced in the connection string, but often, is not used. The connection will be defaulted to 15 seconds. So, its worth resetting those attributes explicitly.
Cn.CommandTimeout = 50
Cn.ConnectionTimeout = 50
One possible solution is to lengthen the connection command timeout value. Your current script has the value set to 0. This could be increased. Running the query in SSMS should give you a rough idea of the time needed to complete the query. Then, adjust the value accordingly.
cm.CommandTimeout = 100
After weeks of testing various code changes, we found that when changing the SQL call to QueryTable method instead of CopyFromRecordset method, it is working fine.
So I am pasting the code if anyone needs it in future.
Sub GetDataset3()
Dim cn As ADODB.Connection
Dim Rs As ADODB.Recordset
Dim UID, PWD, SRV As String
UID = "userId"
PWD = "passworD"
SRV = "192.168.1.1"
If Sheets("data").QueryTables.Count = 0 Then
Sheets("data").Cells.Select
Selection.ClearContents
Dim Str As String 'adds backround query
Str = ""
For Each cell In Range("A1:A10").Cells
Str = Str & Chr(10) & cell
Next
With Sheets("data").QueryTables.Add(Connection:="ODBC;UID=;PWD=;DRIVER=SQL
Server;SERVER=SRV", Destination:=Range("a2"))
.CommandText = "select 1"
'BackgroundQuery = True
'.Refresh BackgroundQuery = True
.FieldNames = False
.AdjustColumnWidth = False
End With
End If
With Sheets("data").QueryTables(1)
.Connection = "ODBC;DRIVER=SQL Server;SERVER=" & SRV &
";database=myDatabaseName;UID=" & UID & ";Pwd=" & PWD &
";Trusted_Connection=no;APP=Microsoft Office"
.CommandText = ("Get_dataset2 '" & Range("dateFrom") & "' ,'" &
Range("dateTo") & "' ")
BackgroundQuery = True
.Refresh BackgroundQuery:=False
End With
End Sub

SQL parameters not returning any results

Following code is not returning any results:
search = "'%" & Request.QueryString("itemname") & "%'"
Set cmd = Server.CreateObject("ADODB.COMMAND")
Set cmd.ActiveConnection = conn
sql = ""
sql = sql & "DECLARE #search varchar;"
sql = sql & "SET #search = ?;"
sql = sql & "SELECT ID, itemname, itemtype FROM vw_items WHERE itemname LIKE #search"
cmd.CommandText = sql
cmd.CommandType = adCmdText
response.write("<strong>You searched for:</strong> " & search & "<br /><br
/>")
cmd.Parameters.Append cmd.CreateParameter("#search", adVarchar, adParamInput, 50, search)
set rs = cmd.Execute
else
search = null
strSQL2 = "SELECT * FROM vw_items"
set rs = server.CreateObject("ADODB.Recordset")
rs.open strSQL2,conn
end if
I've seen this answer: ADO parameterised query not returning any result and tried fixing mine but no luck
Any help would be much appreciated
When using ADODB.Command the provider infers the data type based off the Parameters collection that is setup before calling the Execute() method.
As you are passing in as
search = "'%" & Request.QueryString("itemname") & "%'"
in effect when the SQL is executed by the provider it will look like (because it already knows the Parameter is a VARCHAR data type already)
WHERE itemname LIKE ''%youritemname%''
When you actually want it to be
WHERE itemname LIKE '%youritemname%'
This means the current query is doing a LIKE for the physical string '%youritemname%' rather than doing an actually pattern matching query.
The fix is simple, remove the single quotes from the search variable, like so;
search = "%" & Request.QueryString("itemname") & "%"
Useful Links
A: How to use ASP variables in SQL statement (useful tips when working with the ADODB.Command object)

Quote newly created id in message box after SQL Insert in MS Access

I have a VBA script in MS Access that inserts a new record into a SQL Server table based on some inputs on the form.
Private Sub Allocate_Click()
Dim strSQL As String
strSQL = "INSERT INTO dbo_ALLOCATIONS " _
& "([Employee No],[Device Serial Number],[Start Date]) VALUES " _
& "('" & Me.EmployeeNumber & "', '" & Me.SerialNumber & "', '" & Me.StartDate & "')"
CurrentDb.Execute strSQL, dbFailOnError
Dim ReceiptNo As dao.Recordset
Set ReceiptNo = CurrentDb.OpenRecordset("select ##identity")
MsgBox "Device Successfully Allocated. Receipt ID is " & CurrentDb.OpenRecordset("select ##identity")
End Sub
At the end, I query the ID that was auto-incremented during the insert. I want to then quote this in a Message Box so the user can make use of it elsewhere. How can I get it to quote like that in the MsgBox command? The one I have at the moment causes lots of issues around the fact I can't combine this command, and when using only 'ReceiptNo' it says it's not a string.
There should only ever be a single result in the recordset. Try changing your last line to:
MsgBox "Device Successfully Allocated. Receipt ID is " & ReceiptNo(0)
It is not possible to print an entire recordset. You need to refer to the column that you want to print and loop through the recordset while the end of file is not reached. Then output your column as string. I did not test it though.
While not ReceiptNo.EOF
Msgbox Str(ReceiptNo!Identity)
ReceiptNo.moveNext
Wend

Pulling SQL info into Excel

I have been able in the past to create connections and pull in whole tables or even just a column or two from SQL into Excel.
Now what I want to for a user to input an ID into a Userform and then the VBA to run SQL code grabbing the cooresponding ID, FirstName, LastName. It should then paste that info into the first blank row of A,B,C on the "Entry" sheet.
I am getting an error on this line of code stating: Run-time error '1004' Application-defined or object-defined error.
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:=Array("OLEDB;Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=xxx.xxx.xxx.xxx;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=False;Tag with column collation when possible=False;Initial Catalog=DBName"), Destination:=Sheets("Entry").Range("A1").End(xlDown).Offset(1, 0)).QueryTable
Most of this I do not understand it is simply some hand me down code that I am trying to re-purpose. The old code which still works is this:
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:=Array( _
"OLEDB;Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=xxx.xxx.xxx.xxx;Use Procedure for Prepare=1;Auto " _
, _
"Translate=True;Packet Size=4096;Use Encryption for Data=False;Tag with column collation when possibl" _
, "e=False;Initial Catalog=DBName"), Destination:=Range("Database!$A$1")). _
QueryTable
The difference between these is that instead of just dropping it in one set cell with the code pulling over couple hundred thousand lines of data is I want the code to be in the first blank row and only pull over that one record. But each time it runs it needs to go to the next row.
With the old code it made an actual table which I am guessing is related to the fact that at the end it states QueryTable. I would rather just have the data and not the table format. If there is a way to change it to do this that would be great.
Also in the previous version of this the query only pulled from one table and the .SourceConnectionFile = _ link to the file. The new code will need to link to two tables so there are two files as I was unable to have it make a connection file with two tables selected. If you can help with that as well that would be great.
I am using Excel 2013 Standard and SQL Server 2012. Please let me know if you need any more info.
So This is what I have so far trying the ADO method suggested by #Kyle. The OCR is the variable input from the Userform in previous code. When this runs it gives no error but it paste no data.
Sub Code()
Sheets("Entry").Select
On Error Resume Next
Const adOpenStatic = 3
Const adLockOptimistic = 3
Const adCmdText = &H1
Set objConnection = CreateObject("ADODB.Connection")
Set Objrecordset = CreateObject("ADODB.Recordset")
ConnectionString = "Provider=SQLOLEDB;Data Source=xxx.xxx.xxx.xxx;Initial Catalog=DBName;User ID=MyUN;Password=MyPW"
objConnection.Open
Objrecordset.Open "Select B.ID, B.Firstname, B.Lastname From TableA as A Join TableB as B on A.ID = B.ID Where A.Cardnumber =" & OCR, objConnection, adOpenStatic, adLockOptimistic, adCmdText
If Not Objrecordset.EOF Then
Sheets("Entry").Range("A1").End(xlDown).Offset(1, 0).CopyFromRecordset Objrecordset
Objrecordset.Close
Else
MsgBox "Did not Work"
End If
End Sub
So I was able to get it to work with this:
Sub Code()
Sheets("Entry").Select
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 = "" ' Enter your server name here
Database_Name = "" ' Enter your database name here
User_ID = "" ' enter your user ID here
Password = "" ' Enter your password here
SQLStr = "SELECT B.ID, B.FirstName, B.LastName From Table A Join Table B as B on A.ID = B.ID Where A.CardNumber ='" & OCR & "'" ' 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
With Worksheets("Entry").Range("A1").End(xlDown).Offset(1, 0) ' 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

SQL query that uses a range in excel as the criteria

I use external data from an SQL query in excel to pull data onto a spreadsheet then the spreadsheet can be sent to one of my colleges in say the sales department and they can view the queried information.
I want to be able to have a cell that they can enter data into and press a refresh button.
So I need to do some thing like this:
SELECT Customer.CustomerCode, Customer.Name,
OrderHeader.OrderType, OrderHeader.OrderNumber
FROM Customer INNER JOIN
OrderHeader ON Customer.CustomerID = OrderHeader.CustomerID
WHERE (OrderHeader.OrderType = 2) AND (OrderHeader.OrderNumber = Range("A1").Value)
Not sure how or if this is possible and the reason I need to do this is because i'm going through lines from all the Quotes and if there is over 65536 then i'll have a problem.
This is what I have at the moment the SQL is different but that doesn't matter
The easiest way is to pull the values for the parameters into VBA, and then create the SQL statement as a string with the parameter values, and then populate the commandtext of the query.
Below is a basic example of the parameter in cell A1.
Sub RefreshQuery()
Dim OrderNo As String
Dim Sql As String
' Gets value from A1
OrderNo = Sheets("Sheet1").Range("A1").Value
'Creates SQL Statement
Sql = "SELECT Customer.CustomerCode, Customer.Name, " & _
"OrderHeader.OrderType , OrderHeader.OrderNumber " & _
"FROM Customer " & _
"INNER Join OrderHeader ON Customer.CustomerID = OrderHeader.CustomerID " & _
"WHERE (OrderHeader.OrderType = 2) And (OrderHeader.OrderNumber = " & OrderNo & ") "
With ActiveWorkbook.Connections(1).ODBCConnection
.CommandText = Sql
.Refresh
End With
End Sub
This assumes you have the query in Excel to begin with, and that it's the only query. Otherwise you'll have to define the name of the query as below:
With ActiveWorkbook.Connections("SalesQuery").ODBCConnection
I hope that helps :)
Further to #OWSam's example using ODBC, we can also use ADO to pull back query results from SQL Server. Using ADO, you cannot use windows verification and you will need the user to input their password somehow, to be able to run the query.
Personally I create a userform which has UserName and Password. Username pre-populated using Environ, and then they can type their password into the relevant TextBox.
Sub sqlQuery()
Dim rsConn As ADODB.Connection, rsData As ADODB.Recordset
Dim strSQL As String, winUserName As String, pwd As String
winUserName = UCase(Environ("username"))
pwd = "mypassword" 'password needed here.
strSQL = "SELECT * FROM mydatabase" 'query
Set rsConn = New ADODB.Connection
With rsConn
.ConnectionString = "Provider = sqloledb;" & _
"Data Source = [server name here];" & _
"Initial Catalog = [initial database];" & _
"Integrated Security=SSPI;" & _
"User ID = " & winUserName & ";" & _
"Password = " & pwd & ";"
.Open
End With
Set rsData = rsConn.Execute(strSQL)
Range("A1").CopyFromRecordset rsData
End Sub
Edit: You must go into references and turn on the Microsoft ActiveX Data Objects Recordset 6.0 library (or equivalent within your VBE).

Resources