VBA string to SQL works for 'WHERE from other non-date column'
I've tried several attempts: CAST in query, converting both to integers, etc.
Query returns a 0 or first few columns sometimes are the date range for all rows when running in VBA, but when pasting the VBA SQL string into SQL, it works.
'datecol is type Date in SQL
Dim conn as ADODB.Connection
Dim rs as ADODB.Recordset
Dim strdate, sqldate as String
strdate = "11/10/20"
sqldate = "SELECT * FROM dbTable1 WHERE datecol = '" & strdate & "'";
Set rs = conn.Execute(sqldate)
arrayRows = rs.GetRows
rs.Close
arrayRowsTranspose = Application.Transpose(arrayRows)
Probably date format is not correct, try it this way
strdate = "2020-11-10"
Related
We are trying to fetch record from MS Dataverse using ODBC driver 17/18 for Sql Server into Excel using Macro(VBA) code.
Issue: There are some columns with type nvarchar which are not showing in proper format as it shows for other columns.
I checked the Recordset for nvarchar type column field is showing Field Type = 202 and this Field DataType Enum = adVarWChar and ADO type = A null-terminated Unicode character string.
Code used to fetch records:
Dim con1 As Object
Dim rs1 As ADODB.Recordset
Dim strSql As String
Dim conStr As String
Set con1 = CreateObject("ADODB.Connection")
con1.Open "Driver={ODBC Driver 17 for SQL Server};DataTypeCompatibility=80;Server=xxxxx.crm.dynamics.com;Initial Catalog=xxxxx;TrustServerCertificate=Yes;Authentication=ActiveDirectoryInteractive;Connection Timeout=60;"
strSql = "select * from Account"
con1.CommandTimeout = 0
Set rs1 = New ADODB.Recordset
rs1.ActiveConnection = con1
rs1.CursorType = adOpenForwardOnly
rs1.LockType = adLockReadOnly
rs1.Source = strSql
rs1.Open
Do Until rs1 Is Nothing
WorksheetData rs1
Set rs1 = rs1.NextRecordset
Loop
con1.Close
Set rs1 = Nothing
We need to get this record in Text/String format so we can use it.
I have several queries in an MS Access database. Some of these use parameters. I use the following code in VBA to provide the query with these parameters:
VBA
Dim startDate As Date
Dim endDate As Date
Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim rst As DAO.Recordset
If IsNull(Me.dpFrom) Or IsNull(Me.dpTo) Then
MsgBox "Please select a date!"
ElseIf (Me.dpFrom.Value > Me.dpTo.Value) Then
MsgBox "Start date is bigger than the end date!"
Else
startDate = Me.dpFrom.Value
endDate = Me.dpTo.Value
Set dbs = CurrentDb
'Get the parameter query
Set qdf = dbs.QueryDefs("60 Dec")
'Supply the parameter value
qdf.Parameters("startDate") = startDate
qdf.Parameters("endDate") = endDate
'Open a Recordset based on the parameter query
Set rst = qdf.OpenRecordset()
'Check to see if the recordset actually contains rows
If Not (rst.EOF And rst.BOF) Then
rst.MoveFirst 'Unnecessary in this case, but still a good habit
Do Until rst.EOF = True
'Save contact name into a variable
Me.tbBUDdec.Value = rst!Som
rst.MoveNext
Me.tbLEYdec.Value = rst!Som
rst.MoveNext
Me.tbMDRdec.Value = rst!Som
rst.MoveNext
Me.tbODCdec.Value = rst!Som
rst.MoveNext
Loop
Else
MsgBox "There are no records in the recordset."
End If
rst.Close 'Close the recordset
Set rst = Nothing 'Clean up
Access Query
PARAMETERS startDate DateTime, endDate DateTime;
SELECT WarehouseCode, COUNT(DeliveryPoint) AS Som
FROM [50 resultaat]
WHERE EntryDate between [startDate] and [endDate]
GROUP BY WarehouseCode;
This is working fine. However, I am now trying to use the same code to call a passthrough query to a SQL server. This query uses a different syntax to declare and set the parameters:
SQL Server query
DECLARE #InvLineEntryDateBegin AS date
DECLARE #InvLineEntryDateEnd AS date
SET #InvLineEntryDateBegin = '2017-01-01'
SET #InvLineEntryDateEnd = '2017-05-31'
Select WarehouseCode, Count(PickOrderNr) as Som
FROM ( bla bla bla ...
I can't get my VBA code to work with the different SQL syntax. I've read several options but couldn't find anything concrete. Does anyone have experience with this query structure?
In other words: How can I, in VBA, insert parameters in a stored procedure that queries on a SQL server?
Consider building a named stored procedure that resides in SQL Server and have MS Access call it passing parameters using ADO as opposed to your current DAO method since you require parameterization. Then bind results to a recordset:
SQL Server Stored Proc
CREATE PROCEDURE myStoredProc
#InvLineEntryDateBegin DATE = '2017-01-01',
#InvLineEntryDateEnd DATE = '2017-05-31'
AS
BEGIN
SET NOCOUNT ON;
SELECT WarehouseCode, Count(PickOrderNr) as Som
FROM ( bla bla bla ... ;
END
VBA
' SET REFERENCE TO Microsoft ActiveX Data Object #.# Library
Dim conn As ADODB.Connection, cmd As ADODB.Command, rst As ADODB.Recordset
Dim startDate As Date, endDate As Date
If IsNull(Me.dpFrom) Or IsNull(Me.dpTo) Then
MsgBox "Please select a date!", vbCritical, "MISSING DATE"
Exit Sub
End if
If (Me.dpFrom.Value > Me.dpTo.Value) Then
MsgBox "Start date is bigger than the end date!", vbCritical, "INCORRECT RANGE"
Exit Sub
End if
startDate = Me.dpFrom.Value: endDate = Me.dpTo.Value
' OPEN CONNECTION
Set conn = New ADODB.Connection
conn.Open "DRIVER={SQL Server};server=servername;database=databasename;UID=username;PWD=password;"
' OPEN/DEFINE COMMAND OBJECT
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = conn
.CommandText = "myStoredProc"
.CommandType = adCmdStoredProc
' BIND PARAMETERS
.Parameters.Append .CreateParameter("#InvLineEntryDateBegin", adDate, adParamInput, 0, startDate)
.Parameters.Append .CreateParameter("#InvLineEntryDateEnd", adDate, adParamInput, 0, endDate)
En With
' BIND RESULTS TO RECORDSET
Set rst = cmd.Execute
...
Simply create a pass-though query in Access and save it.
Ensure that the PT query works. It will likely look like:
Exec MySpName '2017-01-01', '2017-05-31'
Again: 100% Make sure the query works when you click on it in Access. At this point you not written any VBA code.
Once you have above pass through query working, then in VBA you can do this:
Dim strStartDate As String
Dim strEndDate As String
Dim strSQL As String
strStartDate = "'" & Format(Me.dpFrom, "yyyy-mm-dd") & "'"
strEndDate = "'" & Format(Me.dpTo, "yyyy-mm-dd") & "'"
strSQL = "exec MyStoreProc " & strStartDate & "," & strEndDate
With CurrentDb.QueryDefs("QryMyPass")
.SQL = strSQL
Set rst = .OpenRecordset
End With
If I remember right, in a pass-through query, you are passing the query definition directly to the engine in which it is going to run. So, you will have to use the SQL Server syntax for your query instead of the Access VBA syntax. Give that a try.
Also, the same goes for a Stored procedure. Use the syntax like you were to execute through SSMS.
"exec sp_mysp var1 var2" and so on.
I have a couple macros to make calls to SSMS 2014 to run a query and return the results in a defined cell in my worksheet. They work successfully, but when I try to use certain queries with temp tables I get the following error message:
I have researched online and the best answer I can find is to add SET NOCOUNT ON at the beginning of my query. I tried that, and still got the same message.
The piece of code that the Debug brings me to is as follows:
bqr.Range("B6").CopyFromRecordset rst
The meat and potatoes of my code, along with the variable setups that matter, is as follows:
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
Dim ConnectionString As String
Dim StrQuery As String
Dim SOURCE As String
Dim DATABASE As String
Dim QUERY As String
Dim intColIndex As Integer
Dim sDate As String
Dim eDate As String
Dim qt As Worksheet
Dim qtr As Worksheet
Dim bqr As Worksheet
Dim bp As Worksheet
ConnectionString = "Provider=SQLOLEDB;Data Source=" & SOURCE & "; Initial Catalog=" & DATABASE & "; Integrated Security=SSPI;"
cnn.Open ConnectionString
cnn.CommandTimeout = 900
StrQuery = QUERY
rst.Open StrQuery, cnn
bqr.Range("B6").CopyFromRecordset rst
For intColIndex = 0 To rst.Fields.Count - 1
Range("B5").Offset(0, intColIndex).Value = rst.Fields(intColIndex).Name
Next
The most confusing part is that the error suggests that my rst recordset is closed, even though it is opened just before I use the CopyFromRecordset
I've tried adding DROP TABLE at the end of my query, the SET NOCOUNT ON function at the beginning, and even tested some smaller simple temp tables as tests.
For example, I set my QUERY variable to:
QUERY = "CREATE TABLE #Test1 (TestID INT, TestValue VARCHAR(20))"
QUERY = QUERY + " INSERT INTO #Test1"
QUERY = QUERY + " VALUES (1, 'Pass'), (2, 'Fail'), (3, 'Try Again')"
QUERY = QUERY + " SELECT * INTO #Test2 FROM #Test1 WHERE TestID = 1"
QUERY = QUERY + " SELECT * FROM #Test2"
Then ran the code to extract and past into Excel, and it worked.
Therefore, I am stumped. Maybe there is a limit to how long the query can be? Right now it's 180 lines long, so it's pretty big...
Any suggestions are appreciated!
EDIT: Full macro below (less the actual query):
Private Sub CommandButton1_Click()
If TextBox1.Value = "i.e. 20160101" Or TextBox2.Value = "i.e. 20160131" Then
MsgBox "Please fill out all fields before proceeding"
ElseIf Len(TextBox1.Value) <> 8 Or Len(TextBox2.Value) <> 8 Or Not IsNumeric(TextBox1.Value) Or Not IsNumeric(TextBox2.Value) Then
MsgBox "Please use correctly formatted Datekeys (i.e. yyyymmdd)"
Else
Application.DisplayAlerts = False
Sheets(ActiveWorkbook.Sheets.Count).Select
While ActiveSheet.Name <> "[worksheet I want to keep]"
ActiveSheet.Delete
Sheets(ActiveWorkbook.Sheets.Count).Select
Wend
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
Dim ConnectionString As String
Dim StrQuery As String
Dim SOURCE As String
Dim DATABASE As String
Dim QUERY As String
Dim intColIndex As Integer
Dim sDate As String
Dim eDate As String
Dim qtr As Worksheet
Dim bqr As Worksheet
Dim bp As Worksheet
Set qtr = Sheets([sheet name])
Sheets.Add after:=qtr
Set bqr = ActiveSheet
bqr.Name = "[sheet name]"
Sheets.Add after:=bqr
Set bp = ActiveSheet
bp.Name = "[sheet name]"
SOURCE = "[server]"
DATABASE = "[database]"
sDate = UserForm1.TextBox1.Value
eDate = UserForm1.TextBox2.Value
QUERY = "[beginning of query]"
QUERY = QUERY + " [more query here]" 'This gets repeated a lot for each additional line in the query'
qtr.Select
Range("B6").Select
While ActiveCell.Value <> ""
QUERY = QUERY + " " + ActiveCell.Value
ActiveCell.Offset(1, 0).Select
Wend
QUERY = QUERY + " [more query here]" 'This gets repeated a lot for the remaining lines in the query'
ConnectionString = "Provider=SQLOLEDB;Data Source=" & SOURCE & "; Initial Catalog=" & DATABASE & "; Integrated Security=SSPI;"
cnn.Open ConnectionString
cnn.CommandTimeout = 2000
StrQuery = QUERY
rst.Open StrQuery, cnn
bqr.Range("B6").CopyFromRecordset rst
For intColIndex = 0 To rst.Fields.Count - 1
Range("B5").Offset(0, intColIndex).Value = rst.Fields(intColIndex).Name
Next
End If
Application.DisplayAlerts = True
End Sub
Start your T-SQL query with set nocount on;
QUERY = "set nocount on;"
QUERY = QUERY & "declare #Test1 table (TestID INT, TestValue VARCHAR(20))"
QUERY = QUERY & " INSERT INTO #Test1"
QUERY = QUERY & " VALUES (1, 'Pass'), (2, 'Fail'), (3, 'Try Again')"
QUERY = QUERY & " SELECT * FROM #Test1 WHERE TestID = 1"
Then it should work. The next example will also work and is a bit closer to your example (yet using table variables).
set nocount on;
declare #Test1 table (TestID INT, TestValue VARCHAR(20))
declare #Test2 table (TestID INT, TestValue VARCHAR(20))
INSERT INTO #Test1
VALUES (1, 'Pass'), (2, 'Fail'), (3, 'Try Again')
insert into #Test2
select *
from #Test1 WHERE TestID = 1
select * from #Test2
I have some simple code that will load all the data from an excel sheet into an array but I am getting an error 94 inproper use of null due to the fact that my source sheet has some blank columns IE: Q through EA are blank columns but A -P and EB - EF have data. (terrible design for an excel sheet being used as a table I know,.. but I didn't do it)
Seeing as I cant redesign the table.. how can I skip the blanks as to avoid causing errors when loading them into my array?
Dim Conn As New ADODB.Connection
Dim mrs As New ADODB.Recordset
Dim DBPath As String, sconnect As String
DBPath = "\\MYPATH\MYFILE.xlsm"
sconnect = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DBPath _
& ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"
Conn.Open sconnect
sSQLSting = "SELECT * From [log$]"
mrs.Open sSQLSting, Conn
'=>Load the Data into an array
ReturnArray = mrs.GetRows
'Close Recordset
mrs.Close
'Close Connection
Conn.Close
The IsNull() function returns True or False. So include it inside Jet/ACE's conditional logic function IIF()
sSQLString = "SELECT IIF(IsNull(Col1), 0, Col1)," _
& " IIF(IsNull(Col2), 0, Col2)," _
& " IIF(IsNull(Col3), 0, Col3)"
& " From [log$];"
#JohnsonJason Why do you need it in a Array? You could just filter your data with Advanced Filter like here or just drop it and loop to get the columns you need. If you don't know how many columns will be you can create a clone Recordset and get the columns Name and create your Query based on that.
The clone RecordSet is something like this:
'' Declare Variables
Dim oRst As ADODB.Recordset, oRstVal As ADODB.Recordset, oStrm As ADODB.Stream
Dim sHeaders as String
'' Set Variables
Set oRst = New ADODB.Recordset
Set oRstVal = New ADODB.Recordset
Set oStrm = New ADODB.Stream
.... [Something else]
'' Save your current Recordset in the Stream
oRst.Save oStrm
'' Assign your Stream to the new Recordset (oRstVal)
oRstVal.Open oStrm
'' Loop trough your Recorset for Columns Name
'' Use an IF or a Select to filter
For iCol = 0 To oRstVal.Fields.Count - 1
sHeaders = sHeaders + "," + oRstVal.Fields(iCol).Name
Next
And use sHeaders in your Statement in to get the columns you need.
''Instead of Select * From ...
sQuery = "Select " + sHeaders + _
"From ...."
I am trying to write a macro to query from our database using the IN clause except with one problem. I am reaching the limit of the IN clause for SQL Server.
My macro looks like this:
Dim row_count As Double
row_count = ActiveSheet.UsedRange.Rows.Count - 1
half_row_count = row_count
Dim i As Double
Dim products As String
For i = 2 To half_row_count
Dim product_id As String
product_id = Cells(i, 1).Value
'test = sixtyDays(product_id, conn)
'Cells(i, 10).Value = test
products = products & "'" & product_id & "'" & ", "
Next i
Dim sample As New ADODB.Recordset
products = Left(products, Len(products) - 2)
Set sample = sixtyDays(products, conn)
Sheets(1).Range("K2").CopyFromRecordset sample
conn.Close
Function sixtyDays(ProductID As String, new_conn As ADODB.Connection) As ADODB.Recordset
Dim sConnString As String
Dim rst As New ADODB.Recordset
Dim recordsAffecfted As Long
StrQuery = "SELECT ProductAnalysisByMonth.SalesQty FROM ProductAnalysisByMonth WHERE ProductAnalysisByMonth.ProductID IN (" + ProductID + ") AND ProductAnalysisByMonth.Month = " + CStr(Month(Date) - 2)
rst.Open StrQuery, new_conn
Set sixtyDays = rst
End Function
So I need to some how split the query into smaller chunks, except, the number of arguments passed to the SQL query will vary from week to week.
What is the most efficient way of handling this problem?
Create a table function that will return your string results into a data-set that can be inserted into a CTE, temp table, or used directly in a join. This has been the most effective way for me to get around this limitation. Below is a link to Ole Michelsen's website who provides a simple but flexible solution.
Link: http://ole.michelsen.dk/blog/split-string-to-table-using-transact-sql.html