Given the VB.net code, combine multiple queries into 1 - sql

Given this code below which returns a first recordset (rs) based on a date range with some values that are then used in the second recordset (rs2) to sum up a cost. Further explanation is below the code:
strSQL = "SELECT job, suffix, isnull(qty_scrapped,0),isnull(qty_released,0), isnull(price,0),co_num FROM vwDashboardsQuality "
strSQL &= " WHERE trans_date >= '" & dtpStartDate.Value & "' AND trans_date <= '" & dtpEndDate.Value & "' "
rs = conn.Execute(strSQL)
While Not rs.EOF
strCONUM = Trim("" & rs("co_num").Value)
strSelectString = "SELECT ISNULL(a_cost,0) FROM jobmatl WHERE job='" & rs("job").Value & "' AND suffix = " & Format(rs("suffix").Value)
rs2 = conn.Execute(strSelectString)
While Not rs2.EOF
dblSumActualMaterialCost = dblSumActualMaterialCost + CDbl(rs2(0).Value)
rs2.MoveNext()
End While
rs2.Close()
rs2 = Nothing
rs.MoveNext()
End While
rs.Close()
rs = Nothing
I want to combine the queries into a single query so I am not hitting the database through the second recordset (rs2) just to sum up something that I know can be done in a single query.
Any tips would be helpful. Thank you in advance.

It looks like you're just needing to do an inner join on the two queries to get one result set.
See if this works. If so, you can eliminate the second query and second inner loop.
strSQL = "SELECT d.job, d.suffix, isnull(d.qty_scrapped,0), isnull(d.qty_released,0)," _
& " isnull(d.price,0), d.co_num, ISNULL(m.a_cost,0)" _
& " FROM vwDashboardsQuality d" _
& " INNER JOIN jobmatl m" _
& " ON d.job = m.job" _
& " AND d.suffix = m.suffix" _
& " WHERE trans_date >= '" & dtpStartDate.Value & "'" _
& " AND trans_date <= '" & dtpEndDate.Value & "'"
You can paste this in Management Studio, replacing dates as applicable to check the results.
SELECT d.job, d.suffix, isnull(d.qty_scrapped,0), isnull(d.qty_released,0), isnull(d.price,0), d.co_num,
ISNULL(m.a_cost,0)
FROM vwDashboardsQuality d
INNER JOIN jobmatl m
ON d.job = m.job
AND d.suffix = m.suffix
WHERE trans_date >= '2015-09-29'
AND trans_date <= '2015-09-30'

From your code I see that you are at the end just running a SUM on all values for jobmatl.a_cost that fulfill a condition set by the where clause. So why not doing everything on the same query? And you will save yourself all the unnecessary iterations on the result set, you are loosing previous CPU time and resources there.
Also, you are not using all other values on the first query, why getting them on the first place? I removed them from the following query.
SELECT SUM(j.a_cost)
FROM vwDashboardsQuality vDQ
INNER JOIN jobmatl j
ON vDQ.job = j.job
AND vDQ.suffix = j.suffix
WHERE vDQ.trans_date >= #startdate
AND vDQ.trans_date <= #enddate;

Related

Date filter returns an invalid character error

I want to execute a query with a date filter in Access VBA.
My first issue was inconsistent datatypes: expected DATE got NUMBER.
I assume Access stores dates as numbers just like Excel.
I found I had to use "#" between the date for the query to recognize it as a DATE. Now I am getting an INVALID CHARACTER error which means the hash # is an invalid character.
I have to pull in the negotiated costs with our vendors that don't have more than 60 days of being expired.
There is also a tblVendors where the user selects the vendor ID (ORDID) they want to pull the info from. That's why I do a loop to pull in every ORDID in that table. I don't push in all the ORDID in one try because it brings a lot of data and doing it in batches runs faster.
Sub GetMaterialCost()
Dim db As Database
Dim rsData As ADODB.Recordset, rsVendor As DAO.Recordset, rsItemCost As DAO.Recordset
Dim strQuery As String
Dim vendorNO As Long, dtDate As Date
Set db = CurrentDb
dtDate = Format(Now() - 60, "m/d/yyyy")
Set rsVendor = db.OpenRecordset("SELECT ORDID, VEN_NAME, USER_ID FROM tblVendors WHERE ACTIVE = TRUE ORDER BY VEN_NAME, ORDID")
Set rsItemCost = db.OpenRecordset("tbl_ItemCost")
ConnectBILL
rsVendor.MoveFirst
Do Until rsVendor.EOF
strQuery = "SELECT MASID, LOCATION, ITEM, ITEM_QTY, ITEM_UOM, ITEM_COST, EXP_DT " _
& "FROM ITEMMASTER INNER JOIN ORDDETAIL ON (ITEMMASTER.ITEM = ORDDETAIL.ITEM) " _
& "WHERE (LOCATION IN (AS1,AS3,AS6) AND TRIM(MASID) = '" & Cstr(rsVendor.Fields("ORDID")) & "' AND EXP_DT >= #paramDate)"
With ComBill
.CommandText = strQuery
Set rsData = .Execute(, Array(dtDate))
end with
'clears previous instance of vendor data by vendor_no if it exists
db.Execute "DELETE * FROM tbl_ItemCost WHERE MASID LIKE '*" & rsVendor.Fields("ORDID") & "*'"
'starts inserting queried data
rsData.MoveFirst
Do Until rsData.EOF
With rsItemCost
.AddNew
.Fields("MASID") = rsData!MASID
.Fields("LOCATION") = rsData!LOCATION
.Fields("ITEM") = rsData!ITEM
.Fields("ITEM_UOM") = Trim(rsData!ITEM_UOM)
.Fields("ITEM_COST") = rsData!ITEM_COST
.Fields("EXP_DT") = rsData!EXP_DT
.Update
End With
rsData.MoveNext
Loop
rsVendor.MoveNext
Loop
End Sub
Correct these two lines:
dtDate = DateAdd("d", -60, Date)
& "WHERE LOCATION IN (AS1,AS3,AS6) AND TRIM(MASID) = '" & CStr(rsVendor.Fields("ORDID") & "' AND EXP_DT >= #" & Format(dtDate, "yyyy\/mm\/dd") & "#) "
Note, that for ADO, string expressions for date values must be formatted using the ISO sequence.
If the field EXP_DT is Text:
& "WHERE LOCATION IN (AS1,AS3,AS6) AND TRIM(MASID) = '" & CStr(rsVendor.Fields("ORDID") & "' AND EXP_DT >= '" & Format(dtDate, "m\/d\/yyyy") & "') "

VBA RecordSet function takes too much time to update record using RecordCount

I have one table and one query. Both have the same data field but table COLUMN names are equal to query's ROW name. I update table from query's row data using the following code successfully but it takes too much time to update as there are more than 50 columns name in the table for each employee-
Set rst1 = CurrentDb.OpenRecordset("SELECT * FROM tblPayRollDataTEMP")
Set rst2 = CurrentDb.OpenRecordset("SELECT * FROM qryEmpVerifySalary ")
Do Until rst1.EOF
rst2.MoveFirst
Do Until rst2.EOF
For l = 0 To rst1.Fields.count - 1
If rst1!EmpID = rst2!EmpID And rst1.Fields(l).Name = rst2!Head And rst1!PayBillID = TempVars!BillID Then
With rst1
rst1.Edit
rst1.Fields(l).Value = rst2!Amount
rst1!totDeductions = DSum("Amount", "qryEmpVerifySalary", "[PayHeadType] = 'Deductions' AND [EmpID] = " & rst2!EmpID & "") + DLookup("NPS", "qryEmpPayEarning", "[EmpID] = " & rst2!EmpID & "")
rst1!totRecoveries = DSum("Amount", "qryEmpVerifySalary", "[PayHeadType] = 'Recoveries' AND [EmpID] = " & rst2!EmpID & "")
rst1!NetPayable = rst1!totEarnings - (Nz(rst1!totDeductions, 0) + Nz(rst1!totRecoveries, 0))
rst1.Update
End With
End If
Next
rst2.MoveNext
Loop
rst1.MoveNext
Loop
Set rst1 = Nothing
Set rst2 = Nothing
How to improve the performance of the code?
You should use a query to update your records. This would be the fastest solution. Normally one would match the EmpID and drag and drop the fields into the update query or use an expression. If you have to group before or other complex stuff split it in more querys (two or three). It doesnt matter thou, because in the end you just execute one update query.
For your code, you can replace the domainaggregate functions. DLookup(), DSum(), etc... these are worst for performance. A simple select statement runs way faster than DLookup(). Here are a few replacements:
Function DCount(Expression As String, Domain As String, Optional Criteria) As Variant
Dim strSQL As String
strSQL = "SELECT COUNT(" & Expression & ") FROM " & Domain
'Other Replacements:
'DLookup: strSQL = "SELECT " & Expression & " FROM " & Domain
'DMax: strSQL = "SELECT MAX(" & Expression & ") FROM " & Domain
'DMin: strSQL = "SELECT SUM(" & Expression & ") FROM " & Domain
'DFirst: strSQL = "SELECT FIRST(" & Expression & ") FROM " & Domain
'DLast: strSQL = "SELECT LAST(" & Expression & ") FROM " & Domain
'DSum: strSQL = "SELECT SUM(" & Expression & ") FROM " & Domain
'DAvg: strSQL = "SELECT AVG(" & Expression & ") FROM " & Domain
If Not IsMissing(Criteria) Then strSQL = strSQL & " WHERE " & Criteria
DCount = DBEngine(0)(0).OpenRecordset(strSQL, dbOpenForwardOnly)(0)
End Function

Setting Max ID criteria in Ms Access SQL

This query does not run at the beginning. Could someone please help look at what is wrong?
If there is any other way to achieve this kindly suggest.
strSQL1 = "SELECT * FROM PharmSales WHERE HospitalNo='" & Me.txtRegNo &
"' And TDate = #" & Format(Me.txtTDate, "M\/dd\/yyyy") &
"# AND SalesItem1 = '" & Me.txtSalesItem1 & "' And
PharmSalesID=
(SELECT MAX(PharmSalesID) FROM PharmSales)"
Set pr = db.OpenRecordset(strSQL1)
With pr
If Not .BOF And Not .EOF Then 'Ensure that the recordset contains records
.MoveLast
.MoveFirst
If .Updatable Then 'To ensure record is not locked by another user
.Edit 'Must start an update with the edit statement
If IsNull(![TotalPaid]) = True And Me.txtGrand_TotalPay.Value >= Me.txtSalesAmt1.Value Then
![DispQty1] = Nz(![DispQty1] + Me.txtSalesQty1.Value, 0)
.Update
ElseIf IsNull(![TotalPaid]) = False And (Me.txtGrand_TotalPay.Value - Me.txtSalesAmt1.Value) >= (txtGrand_TotalFee - Me.txtGrand_TotalPay.Value + Me.txtSalesAmt1.Value) Then
![DispQty1] = Nz(![DispQty1] + Me.txtSalesQty1.Value, 0)
.Update
Else: MsgBox ("Insufficient balance!")
End If
End If
End If
pr.Close
Set pr = Nothing
Set db = Nothing
End With
End Sub
Your SQL checks multiple criteria, but your subquery doesn't have any of these criteria, so it will probably select a record that doesn't conform to your other criteria, causing your recordset to always be empty.
You need to add these criteria to the subquery, not the main query.
Since the subquery will just return one record, you don't have to add them to both.
strSQL1 = "SELECT * FROM PharmSales" & _
" WHERE PharmSalesID=" & _
" (SELECT MAX(PharmSalesID) FROM PharmSales" & _
" WHERE HospitalNo='" & Me.txtRegNo & _
"' And TDate = #" & Format(Me.txtTDate, "M\/dd\/yyyy") & _
"# AND SalesItem1 = '" & Me.txtSalesItem1 & "')"

Access VBA looping through collection, Making SQL statement for each item

I am trying to write code that makes a collection of associate IDs (Associates is the name of my collection). There are 10 associates at any given time, but the collection will change based on who did what work this month. So once the collection has been made, I want to loop through it and make an SQL statement for each item. Some thing kind of like this:
For Each Item In Associates
qryTopSQL = "SELECT TOP " & QA# & _
" Date, ID, [L#], Deal, RndNum FROM tbl_Data WHERE Date Between #" & _
StartDate & "# And #" & EndDate & "# AND ID = " & Associates() & _
" ORDER BY RndNum"
Next Item
So I end up with however many SQL strings, but I'm having problems with this:
Am I writing the ID = " & Associates() & " part correctly?
How will it name these different strings so that I may access them later?
Once it makes these, I'd like to do a UNION ALL query for all the SQL strings. How would I do this?
Please help if you can, it's greatly appreciated. I'm new to collections and arrays and I don't understand some of the things I've found online.
EDIT for an update:
I tried this:
j = 1
k = 1
For Each Item In Associates
If j = 1 And k = 1 Then
qryTopString1 = "SELECT * FROM qryTopSQL_" & k
Else
qryTopString2 = " UNION ALL SELECT * FROM qryTopSQL_" & k
End If
j = j + 1
k = k + 1
Next Item
'
Set qryTopUnionqdef = CurrentDb.CreateQueryDef("qryTopSQLUnion", qryTopString1 & qryTopString2)
But the resulting query is a union between the first and last TopSQLs, and none in the middle. Clearly the loop at this point it the problem but I can't figure out what to do thus far.
In Access there are two ways to create query objects: VBA queries (in code) or stored queries (using ribbon, wizard, or navigation bar).
Essentially, you want to do both. So in order to migrate your VBA SQL strings into actual stored query objects, you must use QueryDefs. Below is how to iterate to dynamically create the 10 Associates queries and one union query.
Dim qryTopqdef As QueryDef, qryTopUnionqdef As QueryDef, findqdf As QueryDef
Dim i as Integer, j as Integer
' DELETE QUERIES IF EXIST
For each findqdf in CurrentDb.Querydefs
If Instr(findqdf.Name, "qryTopSQL") > 0 Then
db.QueryDefs.delete(findqdf.Name)
End if
Next findqdf
' INDIVIDUAL 10 QUERIES
i = 1
For Each Item In Associates
qryTopSQL = "SELECT TOP " & QA# & _
" Date, ID, [L#], Deal, RndNum FROM tbl_Data WHERE Date Between #" & _
StartDate & "# And #" & EndDate & "# AND ID = " & Item & _
" ORDER BY RndNum"
' QUERY NAMES ARE SUFFIXED BY THE ITERATOR COUNT
Set qryTopqdef = CurrentDb.CreateQueryDef("qryTopSQL_" & i, qryTopSQL)
i = i + 1
Next Item
' UNION QUERY
j = 1
For Each Item In Associates
If j = 1 Then
qryTopSQL = "SELECT TOP " & QA# & _
" Date, ID, [L#], Deal, RndNum FROM tbl_Data WHERE Date Between #" & _
StartDate & "# And #" & EndDate & "# AND ID = " & Item & _
" ORDER BY RndNum"
Else
' UNIONS ARE SIMPLY STACKS OF SELECT STATEMENTS OF SAME COLUMN NUMBER AND DATA TYPE
' TOGETHER JOINED BY THE UNION OR UNION ALL CLAUSE
qryTopSQL = qryTopSQL & " UNION SELECT TOP " & QA# & _
" Date, ID, [L#], Deal, RndNum FROM tbl_Data WHERE Date Between #" & _
StartDate & "# And #" & EndDate & "# AND ID = " & Item & _
" ORDER BY RndNum"
End if
j = j + 1
Next Item
Set qryTopUnionqdef = CurrentDb.CreateQueryDef("qryTopSQLUnion", qryTopSQL)
' UNINTIALIZE OBJECTS
Set qryTopqdef = nothing
Set qryTopUnionqdef = nothing
Also - see this SO post on collections vs arrays

Using an ADODB record set to perform a joined update query

In the following code, I would like to join ADODB record set 'rs3' to table 'tblValueChain10' and update 3 different columns based on the values extracted in the ADODB record set 'rs3'. Currently, the update query is not returning anything.
Dim st_Sql3 As String
Dim rs3 As ADODB.Recordset
Set rs3 = New ADODB.Recordset
Dim Max3 As Integer
rs3.Open "SELECT tblRisk05Holding.IDMacroProcesso01, tblRisk05Holding.Level01Risk, Max(tblRisk05Holding.ManualityStatus) AS MaxDiManualityStatus, Max(tblRisk05Holding.RiskProbabilityStatus) AS MaxDiRiskProbabilityStatus, Max(tblRisk05Holding.RiskExposureStatus) AS MaxDiRiskExposureStatus FROM tblRisk05Holding GROUP BY tblRisk05Holding.IDMacroProcesso01, tblRisk05Holding.Level01Risk", CurrentProject.Connection
st_Sql3 = "UPDATE tblValueChain10 INNER JOIN rs3 ON (tblValueChain10.IDMacroProcesso01 = tblRisk05Holding.IDMacroProcesso01) SET L1RiskManuality = " & rs3.Fields(2) & ", L1RiskProbability = " & rs3.Fields(3) & ", L1RiskGravity = " & rs3.Fields(4) & ""
Application.DoCmd.RunSQL (st_Sql2)
rs3.Close
Set rs3 = Nothing
Access never allows you to use a recordset object as a data source in another query. It doesn't matter whether you have an ADO or DAO recordset; you can't do it. And the query type (SELECT, UPDATE, INSERT, etc.) also doesn't matter; you can't use a recordset object as a data source in any query type.
You might get a workable UPDATE by first saving your SELECT statement as a named query, qryRS3. Then revise the UPDATE to INNER JOIN tblValueChain10 to qryRS3. But I'm uncertain whether Access would consider that query to be updatable; the GROUP BY might cause Access to treat it as not updatable. You'll have to test to see.
Although the goal you try to achieve using ADO Recordset in the way presented can't be done (as user #HansUp wrote), you could try the approach with updating your table tblValueChain10 using subquery taken from the rs3 recordset open call.
I hope that the following query :
UPDATE
tblValueChain10
INNER JOIN
(
SELECT
tblRisk05Holding.IDMacroProcesso01,
tblRisk05Holding.Level01Risk,
Max(tblRisk05Holding.ManualityStatus) AS MaxDiManualityStatus,
Max(tblRisk05Holding.RiskProbabilityStatus) AS MaxDiRiskProbabilityStatus,
Max(tblRisk05Holding.RiskExposureStatus) AS MaxDiRiskExposureStatus
FROM
tblRisk05Holding
GROUP BY
tblRisk05Holding.IDMacroProcesso01,
tblRisk05Holding.Level01Risk
) AS qry_Risk05Holding
ON (tblValueChain10.IDMacroProcesso01 = qry_Risk05Holding.IDMacroProcesso01)
SET
tblValueChain10.L1RiskManuality = qry_Risk05Holding.MaxDiManualityStatus,
tblValueChain10.L1RiskProbability = qry_Risk05Holding.MaxDiRiskProbabilityStatus,
tblValueChain10.L1RiskGravity = qry_Risk05Holding.MaxDiRiskExposureStatus
could help you solve your problem.
You should be able to run the above SQL with the following code:
Dim st_Sql3 As String
st_Sql3 = " UPDATE tblValueChain10 INNER JOIN ( "
st_Sql3 = st_Sql3 & " SELECT " _
& " tblRisk05Holding.IDMacroProcesso01, " _
& " tblRisk05Holding.Level01Risk, " _
& " Max(tblRisk05Holding.ManualityStatus) AS MaxDiManualityStatus, " _
& " Max(tblRisk05Holding.RiskProbabilityStatus) AS MaxDiRiskProbabilityStatus, " _
& " Max(tblRisk05Holding.RiskExposureStatus) AS MaxDiRiskExposureStatus "
st_Sql3 = st_Sql3 & " FROM " _
& " tblRisk05Holding " _
& " GROUP BY " _
& " tblRisk05Holding.IDMacroProcesso01, " _
& " tblRisk05Holding.Level01Risk "
st_Sql3 = st_Sql3 & " ) AS qry_Risk05Holding " _
& " ON (tblValueChain10.IDMacroProcesso01 = qry_Risk05Holding.IDMacroProcesso01) " _
& " SET " _
& " tblValueChain10.L1RiskManuality = qry_Risk05Holding.MaxDiManualityStatus, " _
& " tblValueChain10.L1RiskProbability = qry_Risk05Holding.MaxDiRiskProbabilityStatus, " _
& " tblValueChain10.L1RiskGravity = qry_Risk05Holding.MaxDiRiskExposureStatus "
Application.DoCmd.RunSQL (st_Sql3)
The query is being built-up in four stages because as far as I remember there is a restriction in VBA that there can't be too many line breaks _.
Please notice also that the JOIN used in the update query assumes that connecting two tables by IDMacroProcesso01 would ensure that the records are updated in the right way.
I can't check the solution in Acces right now but maybe it could somehow help you, at least a concept. If there were any errors, please write.
You need to loop through the recordset to iteratively update by each record:
Do While NOT rs3.EOF
st_Sql3 = "UPDATE tblValueChain10"_
& " INNER JOIN rs3 ON (tblValueChain10.IDMacroProcesso01 = tblRisk05Holding.IDMacroProcesso01)" _
& " SET L1RiskManuality = " & rs3.Fields(2) & ", L1RiskProbability = " & rs3.Fields(3) & "," _
& " L1RiskGravity = " & rs3.Fields(4) & ""
DoCmd.RunSQL (st_Sql3)
rs3.MoveNext
Loop
Also please note, your RunSQL () command is calling the incorrect SQL string.
If no err appears, then try to place this code:
Set rs3 = New ADODB.Recordset
in form_load, as in,
private sub Form_load()
Set rs3 = New ADODB.Recordset
End Sub
note: this sample is in vb6.0. that's what I can do. Besides, check the version in references