aggregation query for recordset in VBA - vba

Can I do aggregation query for a recordset in VBA. the first part code is the following:
'' part 1
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Open the connection and execute.
conn.Open sConnString
Set rs = conn.Execute("SELECT * FROM Table1;") ''' this SQL Statement is a simplified one;the actual one is very complex.
RS will be display in sheet 1; then I want to display the result of the following in sheet2:
select sum(col1), count(*), sum(col2) from
(select * from table1) group by 1,2 order by 1,2
because rs has been got from the database in part 1, I does not want to re-run the SQL in database, instead I want to get the result of part 2 in VBA's recordset.
I check the method of recordset, it only provides the method of count, move,etc.
Anyone can help me? thanks!

Related

Conditional inner join statement (VBA/SQL) to generate multiple values

I'm quite new to VBA/SQL and I'm trying to execute a conditional inner join.
I have two tables with a column in common ("CRM" and "CodeCRM") and I would like to obtain an email adress from table2 ("Desks") when something is triggered (CodeBlocage = 101) in table1 ("Flux") in order to add it to an automatic email.
Dim StrDestinataire As String
Select Case Strtable
Case "Flux", "GAFIJOUR"
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim Y As String
Dim sSql As String
Set cn = CurrentProject.Connection
sSql = "Select AddMailCRM from Desks Inner Join Flux on Desks.CODECRM = Flux.CRM WHERE Flux.CODEBLOCAGE = '101'"
Set rs = cn.Execute(sSql)
If Not rs.BOF And Not rs.EOF Then
Y = rs.Fields("AddMailCRM").Value
End If
StrDestinataire = Y
cn.Close
Everything works great except that it should be returning more than one value for the email adress. Any leads?
Thank you
There are three keywords that may cause confusion:
SELECT in sql
SELECT determines the columns in the resulting recordset. If your sql statement is SELECT Name, Number FROM Employees, the SELECT part tells you that the resulting recordset will have two columns named Name and Number.
Select Case in VBA
Select Case is a programming construct for conditionals. You'd use it when you don't want to use a bunch of If..ElseIf..Else statements, but anything you can do with If you can do with Select Case.
Select Case A
Case "Flux"
Execute these VBA statements when the variable A = Flux
Case "Capacitor"
Execute these statements when A = Capacitor
Case Else
Execute these statements when A is neither Flux nor Capacitor
End Select
CASE in sql
The CASE keyword in sql is like Select Case in VBA, but it's used in the field list of a SELECT sql statement (for one).
SELECT Name, CASE WHEN Number = 1 THEN 'One' ELSE 'Two' END MyNum FROM Employees
If you execute this recordset, you get two columns (Name, MyNum). The MyNum field will contains the text One when Number is 1 for that record and the text Two if Number is anything but 1.
Recordsets
You have both Excel and Access tags, so I'm going to assume you're using ADO in either one of those. Your statement
Y = Select email from table2 Inner Join table1 on table2.Crm = table1.Crm WHERE table1.Code = 1
Doesn't do anything - it wouldn't compile. Let's assume you want a variable, Y, to contain the email that would be returned if you executed that sql statement.
Sub GetEmail()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim Y As String
Dim sSql As String
Set cn = New ADODB.Connection
cn.Open "MyConnectionStringGoesHere"
sSql = "Select email from table2 Inner Join table1 on table2.Crm = table1.Crm WHERE table1.Code = 1"
Set rs = cn.Execute(sSql)
If Not rs.BOF And Not rs.EOF Then
Y = rs.Fields("email").Value
End If
End Sub
In this case I have to create a recordset and execute that recordset for a certain connection. Presumably the join and the WHERE clause ensures that it will only return one record. But if it returns more, this example will only use the email from the first record.
Before I grab the Value of the email field, I make sure that the recordset returned at least one record. If it's at the beginning of the file (BOF) and at the end of the file (EOF) at the same time, that means there are no records in the recordset.
Solved.
Dim StrDestinataire As String
Select Case Strtable
Case "Flux", "GAFIJOUR"
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim Y As String
Dim sSql As String
Set cn = CurrentProject.Connection
sSql = "Select AddMailCRM from Desks Inner Join Flux on Desks.CODECRM = Flux.CRM WHERE Flux.CODEBLOCAGE = '101'"
Set rs = cn.Execute(sSql)
If Not rs.BOF And Not rs.EOF Then
Y = rs.Fields("AddMailCRM").Value
End If
StrDestinataire = Y
cn.Close

Excel VBA ADODB sql query using CTE returning no records

This is my first question on Stack Overflow. I have learned a lot from from this site but I have not been able to find the answer for a problem I am having.
I have a SQL query that works in SQL Server 2008 R2 management studio but it does not work when I query it using vba in EXCEL 2013. The query contains a common table expression and it is not returning any records to my recordset.
The SQL query is:
WITH cte AS
( SELECT *, ROW_NUMBER() OVER (PARTITION BY [partNumber]
ORDER BY [date] DESC) AS i FROM [myDB].[dbo].[PartOrders]
WHERE [partDescription] like '%motor%' )
SELECT * FROM cte WHERE i = 1
I have a reference for Microsoft ActiveX Data Objects 6.1 Library
The VBA code I am using is:
Dim conn as ADODB.Connection
Dim sql as String
Dim rst as ADODB.Recordset
Set conn = New ADODB.Connection
conn.ConnectionString = myConnectionString
conn.Open
sql = ";WITH cte AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY " & _
"[partNumber] ORDER BY [date] DESC) AS i " & _
"FROM [myDB].[dbo].[PartOrders]" & _
"WHERE [partDescription] like '%motor%' ) " & _
"SELECT * FROM cte WHERE i = 1 "
Set rst = New ADODB.Recordset
rst.Open sql, conn, adOpenStatic, adLockReadOnly, adCmdText
debug.print rst.recordcount
conn.Close
Set rst = Nothing
Set conn = Nothing
My code prints a "-1" in the Immediate window
I have added the preceding ";" to my query based on a recommendation of another questions response. It does not make a difference.
I have verified the following query string returns a recordset:
sql = "SELECT *, ROW_NUMBER() OVER (PARTITION BY " & _
"[partNumber] ORDER BY [date] DESC) AS i " & _
"FROM [myDB].[dbo].[partNumbers]" & _
"WHERE [partDescription] like '%motor%'"
I am using a CTE due to needing to collect entire records of a table but only of distinct part descriptions. I do not want to see that a motor was ordered 20 times. I would like to see that the motor was was ordered at least once along with the other fields associated with it. I am searching a table with 730,000 records where there are records for 10,000 motors but only 500 distinct types.
I am open to using a different query if it will net the same results but I am really curious as to why my current query is not yielding any records. I hope it is not due to an ADODB and VBA incompability.
Thank you for all of the help I have received from others questions and I appreciate any help you can provide me.
Parfait provided a solution for me in the comments to my original question. Changing my connection string provider to a driver did not work. It caused a Run-time error '-2147467259 (80004005)'
Instead of a CTE, I utilized a derived table in my query.
The SQL query that returns a recordcount for me in VBA is:
SELECT main.*
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY [partNumber]
ORDER BY [date] DESC) AS i FROM [myDB].[dbo].[PartOrders]
WHERE [partDescription] like '%motor%')
AS main WHERE main.i = 1
Thank You!
I had the same issue except I was using Driver as recommended by user7638417. When the recordset returned, the recordcount was -1, but, when I inserted the rows into the spreadsheet, they were all there (24). Used simplified query in example, actual query was more complex which lead me to the CTE method.
Dim sql As String
Dim dbConn As New ADODB.Connection
Dim dbRS As New ADODB.Recordset
Dim rows As Integer
sql = "WITH S1 AS ( SELECT Lot, CollectDt, Mark, Slot, Thick1, Thick2 From Table1 )"
sql = sql + " WHERE Lot = 'lotnumber' "
sql = sql + " SELECT * FROM S1 WHERE Thick2 <> "UNKNOWN" ORDER BY Slot "
dbConn.ConnectionString = "Driver={SQL Server};server=myserver;database=production;uid=guest;pwd=guest"
dbConn.Open
dbConn.CommandTimeout = 600
dbRS.Open sql, dbConn, adOpenStatic, adLockReadOnly
Dim totRow As Integer
totRow = dbRS.RecordCount + 2
' If (dbRS.RecordCount > 0) Then
Sheet1.Cells(2, 1).CopyFromRecordset dbRS
' End If
I had to comment out the check for data in order to get the data from the record set. The value in totRow came back as 1 instead of 26, the expected value which causes other things to fail later in the code.
SQL Server Version: 13.0.4001.0
Excel Version: 2016

Edit records in ADODB recordset

What I am trying to do is to get some data from an online server through an SQL Query and then loop through the recordset modifying the records.
I get an error when trying to modify the recordset:
"Multiple-Step operation generated errors. Check each status value."
My question is: Is there a way of modifying a record from a recordset that I got from a Query?
In this case I am modifying field 2 if field 1 meets a certain criteria. (In this case Field 2 is a string)
Here is the simplified code:
Dim adoConn As ADODB.Connection
Dim locRS As New ADODB.Recordset, proRS As ADODB.Recordset
Dim strConnection As String
Set getSQL = New ADODB.Recordset
'Set Objects
Set adoConn = New ADODB.Connection
'Specify connection string
strConnection = "User ID=xxx; Password=xxx;Data Source=xxx;Provider=OraOLEDB.Oracle"
'Open the connection
adoConn.Open (strConnection)
'Set up recordset properties
getSQL.CursorType = adOpenStatic
getSQL.CursorLocation = adUseClient
getSQL.LockType = adLockBatchOptimistic
'Import the data
getSQL.Open "SELECT FIELD1, FIELD2 FROM TABLE", adoConn, adOpenStatic, adLockOptimistic
Set getSQL.ActiveConnection = Nothing
getSql.Update
'Loop through data
getSQL.MoveFirst
Do While Not stockRS.EOF
'If cetrain condition is met then modify the null column
if getSQL!FIELD1=CRITERIA then
'Error here
getSQL!FIELD2="SOME STRING"
End If
getSQL.MoveNext
Loop
'Close
adoConn.Close
Set adoConn = Nothing
Your SQL is not doing what you think:
SELECT ... NULL OUTCOME ... is going to return the value NULL in a field called OUTCOME but will not link to a field in the table called OUTCOME (which is what I think you are looking for) as your current syntax is setting up an ALIAS not selecting the field. I am assuming the field OUTCOME exists on the table. If not you need to create it up front or do an alter table to add the field before you can write anything to it.
I recommend creating field up front (which I think you have already done). But make sure that the default value is NULL so you don't need to do your NULL trick in the select ALSO make sure that the field is allowed to take a NULL value or you will see errors. Select becomes:
getSQL.Open "SELECT FIELD1, FIELD2, OUTCOME FROM TABLE", adoConn, adOpenStatic, adLockOptimistic
And then manage the NULL value in the function as follows:
if getSQL!FIELD1=CRITERIA then
'Error here
getSQL!OUTCOME="SOME STRING"
ELSE
getSQL!OUTCOME=NULL
End If
This ensure that you always write something to OUTCOME field so processing and OUTCOME don't get out of sync.
Also I still think that you have divorced the recordset data from the server when you:
Set getSQL.ActiveConnection = Nothing
Do this to release resources after your are done.
You may also need a
getSql.Update
After making changes to commit them back to database.

Getting SQL query results in MS Access but not in VBScript

When I perform a SQL Query through MS Access I get results returned but if I perform the same query in VBScript my RecordCount is -1. I can't tell if this is a connection error. I'm not getting any but it's clear that the SQL does return results in Access. I'm getting 0 hits in the below connect code.
sqlquery = "SELECT * FROM i2cner WHERE Authors Like 'Ish*';"
dim conn
set conn=Server.CreateObject("ADODB.Connection")
conn.Provider="Microsoft.Jet.OLEDB.4.0"
conn.Open "d:/inetpub/ga/sarina/i2cner/contacts2000.mdb"
set r = Server.CreateObject("ADODB.recordset")
if r.state = 1 then r.close
r.Open sqlquery, conn
hits = r.RecordCount
session("hits") = hits
set session("r") = r
r.CursorLocation = 3 'adUseClient. Thanks #HansUp
Add the above line before using r.Open.
The CursorLocation is adUseServer. As a result, records are fetched as you progress (similar to .net datareader). Changing it to adUseClient will bring all records on the client side, which will give correct RecordCount.
EDIT: Also, it isn't meaningful to store recordset in session. And, you should close the connection once you are done using it -
conn.Close
set conn = Nothing
What is the need to store recordset in session?
If you just need the number of record counts then you can give
sqlquery = "SELECT COUNT(*) AS cnt FROM i2cner WHERE Authors LIKE 'Ish%'"
Note that there is no ; in the SQL string.
When you have to retrieve the count, you can just have
hits = r.fields("cnt")
OR
hits = r("cnt")
Recordcount is sometimes deceptive, so I don't use it much. I use the above approach every time.

VBA SQL select query but only show some rows

I have a table that contains data that I am trying to import into a spreadsheet control on a userform in vba/excel.
The results will be viewed by an end user, so I have set the value of the header cells on initialization as opposed the the column headings from the sql table.
My query looks something like this
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
sConnString = "Provider=SQLOLEDB;Data Source=mysource;Initial Catalog=mydatabase;User Id=myusername;Password=mypassword;QuotedID=No"
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
conn.Open sConnString
Set rs = conn.Execute("SELECT * FROM WOFT_tbl_clients WHERE userid = '" & UserId.Value & "';")
If Not rs.EOF Then
/////////////////
/something here!/
/////////////////
Else
MsgBox "Error: No records returned.", vbCritical
End If
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
What I am trying to do is get the output of selected columns from the database and be able to feed them into whatever colomn I like on the spreadsheet control.
So in effect I would somehow like a loop that allows me to output the resultset of columns id, name and userid into the spreadsheet control starting from row 2. My database also contains many other columns which are not needed in this spreadsheet, but will be needed for another spreadsheet control on the same userform, some of which will appear on both.
What I would like to be able to do is have each column in its own recordset, so I could have something like ids stored in a id recordset which I could then use in column A in spreadsheet control 1, and colomn 6 in spreadsheet control 2?
I hope this makes sense! I am using Excel 2010
To have each data column from one table in an extra recordset would lead you to isolated data lists, which wouldn't follow any logic of a relational database model. If the data indeed is isolated in the way, that id is a list, name is another list and so on, living in the same table, then - forgive me - your database model is quite bad.
If you nevertheless want to get only certain columns from your SQL database, specify them in the SQL statement:
SELECT id, name, userid FROM tablename WHERE anycondition ORDER BY name
To loop through every record the recordset object returns, use a do-until loop like this. Don't forget the MoveNext, or it may result in an endless loop.
Do Until rs.EOF
... do anything you want with the record ...
rs.MoveNext
Loop
I hope this is what you needed. Feel free to further explain, if I didn't understand your needs correctly.