Query to return the Max(Date) and other table fields based on a primary table list - sql

I am hoping somebody out there can help me with a query problem I can’t seem to resolve;
I have two tables;
Table1
T1_ID
T1_Serial
Table2
T2_ID
T1_ID –lookup to Table 1
T2_Date
T2_History
Table1 Data might look something like this;
T1_ID T1_Serial
1, ABC1
2, ABC2
3, ABC3
4, ABC4
Table2 Data might look like this;
T2_ID, T1_ID, T2_Date, T2_History
1, 1, 05/05/15, “Some History1”
2, 1, 05/17/15, “Some History2”
3, 2, 05/09/15, “Some History3”
4, 2, 05/21/15, “Some History4”
5, 3, 05/12/15, “Some History5”
I would like to set up a query to give me the record containing Max(Date) of table History for each record in table Units
Table1.T1_Serial , Max(T2_Date), T2_History;
For this example;
ABC1 05/17/15 “Some History2”
ABC2 05/21/15 “Some History4”
ABC3 05/12/15 “Some History5”
I have constructed the SQL to give me the T1_Serial and Max(Date), which is working correctly;
SELECT Table2.T1_ID, Max(Table2.T2_Date) AS MaxDate
FROM Table2
GROUP BY Table2.T1_ID;
But when I try adding the T2_History to the query I end up getting all the other history besides the Max(Date).
Hope someone can lead me on the right path. Thanks!

I use similar approach as Sam. But removed the T2_ID from the wrapped SQL to avoid issues with the GROUP BY. Also Join by Date.
SQL Fiddle
WITH MaxDate (T1_ID, MaxDate) as
(
SELECT T1_ID, Max(Table2.T2_Date) AS MaxDate
FROM Table2
GROUP BY Table2.T1_ID
)
SELECT T1_Serial, T2_Date, T2_History
FROM
MaxDate
INNER JOIN Table2 ON MaxDate.MaxDate = Table2.T2_Date
INNER JOIN Table1 ON MaxDate.T1_ID = Table1.T1_ID;
vba version fiddle
SELECT tbl_RMAunit.RMA_SN, tbl_History.Hist_Date,tbl_History.Hist_History
FROM (
SELECT RMA_ID, Max(tbl_History.Hist_Date) as MaxDate
FROM tbl_History
Group by tbl_History.RMA_ID
) MaxDate
Inner Join tbl_History on MaxDate.MaxDate = tbl_History.Hist_Date
Inner Join tbl_RMAunit on MaxDate.RMA_ID = tbl_RMAunit.RMA_ID

I found this example for nested queries on VBA. But i couldnt try it. Good luck.
Sub TestNestedQuery()
Dim RS As DAO.Recordset, strSql As String
Dim qdfTemp As QueryDef
Dim qdfNew As QueryDef
Dim strSQL1 as string
Dim strSQL2 as string
strSQL1 = "SELECT RMA_ID, Max(tbl_History.Hist_Date) as MaxDate " & _
"FROM tbl_History " & _
"Group by tbl_History.RMA_ID"
strSQL2 = "SELECT tbl_RMAunit.RMA_SN, tbl_History.Hist_Date,tbl_History.Hist_History " & _
"FROM qryTemp " & _
"Inner Join tbl_History on qryTemp.MaxDate = tbl_History.Hist_Date " & _
"Inner Join tbl_RMAunit on qryTemp.RMA_ID = tbl_RMAunit.RMA_ID "
With CurrentDb()
Set qdfTemp = .CreateQueryDef("qryTemp", strSQL1) '' SQL 1
Set qdfNew = .CreateQueryDef("qryNew", strSQL2)' SQL 2
GetrstTemp qdfNew
''' Delete the two querydefs if necessary
.QueryDefs.Delete qdfTemp.Name
.QueryDefs.Delete qdfNew.Name
End With
End Sub
Function GetrstTemp(qdfTemp As QueryDef)
Dim rstTemp As DAO.Recordset
Dim i As Integer: i = 0
With qdfTemp
Debug.Print .Name
Debug.Print " " & .sql
'' Open Recordset from QueryDef.
Set rstTemp = .OpenRecordset(dbOpenSnapshot)
Do While Not rstTemp.EOF
i = i + 1
Debug.Print rstTemp.Fields("RMA_SN") '' Change the field name
Debug.Print rstTemp.Fields("Hist_Date") '' Change the field name
rstTemp.MoveNext
Loop
Debug.Print
Debug.Print " Number of records = " & _
rstTemp.RecordCount
End With
End Function

Related

Combine SQL queries if a column is NULL

I have the following VBA code:
Set cRec = g_cDataBase.SqlQuery("SELECT sEmployeeName, lArchitectId FROM tblBuilders WHERE Id = " & lDbId)
If cRec.RecordCount = 0 Then
Exit Function
End If
If IsNull(cRec("sEmployeeName")) Then
Set cRec = g_cDataBase.SqlQuery("SELECT sEmployeeName FROM tblArchitects WHERE Id = " & cRec("lArchitectId"))
If cRec.RecordCount = 0 Then
Exit Function
End If
End If
Can i combine the queries to a single query? When an sEmployeeName is null in the tblBuilders table it needs to give the sEmployeeName from the tblArchitects table based on the first query lArchitectId.
Thanks in advance.
You seem to want a JOIN:
SELECT b.sEmployeeName, b.lArchitectId, a.sEmployeeName
FROM tblBuilders as b LEFT JOIN
tblArchitects as a
ON a.id = b.lArchitectId
WHERE b.Id = " & lDbId;
EDIT:
If you want one architect, then use a correlated subquery:
SELECT b.sEmployeeName, b.lArchitectId,
NZ(b.sEmployeeName,
(SELECT TOP (1) a.sEmployeeName
FROM tblArchitects as a
WHERE a.id = b.lArchitectId
)
) as imputed_employeename
FROM tblBuilders as b
WHERE b.Id = " & lDbId;
Note: Without an ORDER BY in the subquery this returns an arbitrary name. If you have a definition of "first" use that in the ORDER BY.
Consider a conditional IIF + IS NULL logic between both employee name columns. Then, save below SQL as a stored query and pass parameters in VBA via QueryDefs parameters
SQL
PARAMETERS idparam LONG;
SELECT IIF(b.sEmployeeName IS NULL,
a.sEmployeeName,
b.sEmployeeName) AS notnullEmployeeName
FROM tblBuilders b
LEFT JOIN tblArchitects a
ON b.Id = a.lArchitectId
WHERE b.Id = idparam
If all rows of tblBuilders can potentially be missing compared to tblArchitects by same corresponding Id, then run MS Access equivalent of full join:
Full Join Query
SELECT b.Id, b.sEmployeeName AS bldsEmployeeName
, a.lArchitectId, a.sEmployeeName AS archsEmployeeName
FROM tblBuilders b
LEFT JOIN tblArchitects a
ON b.Id = a.lArchitectId
UNION
SELECT b.Id, b.sEmployeeName AS bldsEmployeeName
, a.lArchitectId, a.sEmployeeName AS archsEmployeeName
FROM tblBuilders b
RIGHT JOIN tblArchitects a
ON b.Id = a.lArchitectId
Final Query
PARAMETERS idparam LONG;
SELECT IIF(bldsEmployeeName IS NULL,
archsEmployeeName,
bldsEmployeeName) AS notnullEmployeeName
FROM myfulljoinquery
WHERE Id = idparam
OR lArchitectId = idparam
VBA
Dim cRec As Recordset
Dim qdef As QueryDef
Set qdef = CurrentDb.QueryDefs("myFinalSavedQuery")
' BIND PARAMS
qdef!idparam = lDbId
' CREATE RECORDSET
Set cRec = qdef.OpenRecordset()
' ... DO STUFF ON cRec ...
cRec.close()
Set qdef = Nothing: Set cRec = Nothing

Access Query with complex sorting requirements

I have to generate Q-Q plots from a continually expanding dataset. In order to do this I have to have two columns sorted independently of each other.
Like this:
Main table
F1 | F2
---+----
6 | 10
8 | 7
2 | 5
The final table should sort like this:
F1 | F2
---+----
2 | 5
6 | 7
8 | 10
I attempted to do this with two joined subqueries and a VBA function that added a row number to each subquery. It did not return any values, although each subquery works on its own.
This is the SQL query that I attempted to use:
SELECT
wautonumber([Au_org_gpt]) AS index_no,
ORG.Project, ORG.Au_org_gpt AS Au_org_gpt_QQ,
MTS.Au_mts_gpt AS Au_mts_gpt_QQ
FROM
(SELECT
wautonumber([Au_org_gpt]) AS index_no,
MCIPLD_geochem_mts.Project, MCIPLD_geochem_mts.Au_org_gpt
FROM
MCIPLD_geochem_mts
ORDER BY
MCIPLD_geochem_mts.Au_org_gpt) AS ORG
INNER JOIN
(SELECT
wautonumber([Au_mts_gpt]) AS index_no,
MCIPLD_geochem_mts.Project, MCIPLD_geochem_mts.Au_mts_gpt
FROM
MCIPLD_geochem_mts
ORDER BY
MCIPLD_geochem_mts.Au_mts_gpt) AS MTS ON ORG.index_no = MTS.index_no;
Assuming both fields have data in every record.
If both fields have unique number values, consider:
SELECT Query1.Seq, Query1.F1, Query2.F2
FROM
(SELECT DCount("*","Main","F2<=" & [F2]) AS Seq, Main.F2
FROM Main ORDER BY Main.F2) AS Query2
INNER JOIN
(SELECT DCount("*","Main","F1<=" & [F1]) AS Seq, Main.F1
FROM Main ORDER BY Main.F1) AS Query1
ON Query2.Seq = Query1.Seq
ORDER BY Query1.Seq;
Another query approach requires two query objects:
Query1
SELECT "F1" AS Src, F1 AS Data FROM Main
UNION SELECT "F2", F2 FROM Main;
Query 2
TRANSFORM First(Query1.Data) AS FirstOfData
SELECT DCount("*","Query1","Src='" & [Src] & "' AND Data<=" & [Data]) AS Seq
FROM Query1
GROUP BY DCount("*","Query1","Src='" & [Src] & "' AND Data<=" & [Data])
PIVOT Query1.Src;
Both solutions might perform slowly with large dataset.
Here is VBA approach. Build a table called MainSorted with 3 fields:
Seq - autonumber
F1 - number
F2 - number
Sub MainSort()
Dim rsSrc As Recordset, db As Database
Dim lngRow As Long
Set db = CurrentDb
db.Execute "DELETE FROM MainSorted"
db.Execute "INSERT INTO MainSorted(F1) SELECT F1 FROM Main ORDER BY F1"
Set rsSrc = db.OpenRecordset("SELECT F2 FROM Main ORDER BY F2")
lngRow = DMin("Seq", "MainSorted")
While Not rsSrc.EOF
db.Execute "UPDATE MainSorted SET F2=" & rsSrc!F2 & " WHERE Seq = " & lngRow
rsSrc.MoveNext
lngRow = lngRow + 1
Wend
End Sub
Or this version:
Sub MainSort()
Dim rsSrc1 As Recordset, rsSrc2 As Recordset, db As Database
Set db = CurrentDb
db.Execute "DELETE FROM MainSorted"
Set rsSrc1 = db.OpenRecordset("SELECT F1 FROM Main ORDER BY F1")
Set rsSrc2 = db.OpenRecordset("SELECT F2 FROM Main ORDER BY F2")
While Not rsSrc1.EOF
db.Execute "INSERT INTO MainSorted(F1,F2) VALUES(" & rsSrc1!F1 & "," & rsSrc2!F2 & ")"
rsSrc1.MoveNext
rsSrc2.MoveNext
Wend
End Sub
Considering #CPerkins comments:
Sub MainSort()
Dim rsSrc1 As Recordset, rsSrc2 As Recordset, rsDest As Recordset, db As Database
Set db = CurrentDb
db.Execute "DELETE FROM MainSorted"
Set rsSrc1 = db.OpenRecordset("SELECT F1 FROM Main ORDER BY F1")
Set rsSrc2 = db.OpenRecordset("SELECT F2 FROM Main ORDER BY F2")
Set rsDest = db.OpenRecordset("SELECT * FROM MainSorted")
While Not rsSrc1.EOF
rsDest.AddNew
rsDest!F1 = rsSrc1!F1
rsDest!F2 = rsSrc2!F2
rsDest.Update
rsSrc1.MoveNext
rsSrc2.MoveNext
Wend
End Sub

Update record if matching values in same table

I have a MS Access database with a list of transactions. I am trying to update a "Match" field on both records where they have some of the same values in fields (Document Number, Voucher Number, Subhead) but opposite amounts. It also needs to avoid duplicates.
Document Number Amount ABS Match
N6809561990112 438.48 438.48
N6809561990112 438.48 438.48
N6809561990112 -438.48 438.48
What I the end result after the SQL should look like
Document Number Amount ABS Match
N6809561990112 438.48 438.48 Y
N6809561990112 438.48 438.48
N6809561990112 -438.48 438.48 Y
The table name is "tblUnmatched"
I tried the following but it updated every record in the table to "Y"
strSql = "Update tblUnmatched SET match = 'Y' WHERE EXISTS(select * " & _
"from tblUnmatched t1 " & _
"inner join tblUnmatched t2 on " & _
"t1.[DOCUMENT NUMBER] = t2.[DOCUMENT NUMBER]" & _
"where t1.ABS = t2.ABS AND t1.AMOUNT <> t2.AMOUNT AND t1.SUBH = t2.SUBH)"
DoCmd.RunSQL strSql
I also tried this but it couldn't handle the duplicate problem.
strSql = "Update tblUnmatched SET match = 'Y' WHERE [DOCUMENT NUMBER] IN(select t1.[DOCUMENT NUMBER] " & _
"from tblUnmatched t1 " & _
"inner join tblUnmatched t2 on " & _
"t1.[DOCUMENT NUMBER] = t2.[DOCUMENT NUMBER]" & _
"where t1.ABS = t2.ABS AND t1.AMOUNT <> t2.AMOUNT AND t1.SUBH = t2.SUBH)"
DoCmd.RunSQL strSql
Your task is impractical in Access SQL without a primary key field. Although there is no such key in the data source you import, that does not prohibit you from adding one in Access.
Add an autonumber primary key, id, to tblUnmatched. Then for each new batch of incoming data:
import into a scratch table, tblScratch
DELETE FROM tblUnmatched
append the rows from tblScratch into tblUnmatched
(The process could be cleaner if you can use SELECT FROM <your data source> to append directly to tblUnmatched, instead of first importing to tblScratch.)
Save the following SELECT statement as qryFindMatches:
SELECT
sub.[Document Number],
sub.Amount,
sub.MinOfid,
DCount(
"*",
"tblUnmatched",
"[Document Number]='" & [Document Number]
& "' AND Amount = -1 * " & [Amount]
) AS match_count
FROM
(
SELECT [Document Number], Amount, Min(id) AS MinOfid
FROM tblUnmatched
GROUP BY [Document Number], Amount
) AS sub;
Then the UPDATE you want can be fairly easy to create. Beware the performance may not be blazing fast; hopefully you can accommodate it once a month.
UPDATE tblUnmatched
SET [Match] = True
WHERE id In
(
SELECT MinOfId
FROM qryFindMatches
WHERE match_count > 0
);
I added an additional row to your sample data and tested with Access 2007. I think this is what you want ...
id Document Number Amount Match
1 N6809561990112 $438.48 True
2 N6809561990112 $438.48 False
3 N6809561990112 ($438.48) True
4 N6809561990112 $5.00 False

SQL Sort table and Update Column

Hi can anyone tell me the how I can do the following with SQL In an MS Access Database.?
I have a table that looks like this:
MyTable
Plan Item Sort_Number
-------------------------------------
11026 ZSC1753D
11018 PS13-2
11026 ZSC1753B
11018 PSHH278B
11026 ZSO1753A
11018 PS3169
11027 ZSC1754B
11027 ZSC1754A
11026 ZSC1753A
11026 ZSC1753C
I have a table (above) which needs to be sorted first by the Plan (Column 1), then by Ascending order of Item (Column 2) and the third column Sort Number needs to be populated with sequential numbers of 1 to xx.. for each of the different plans.
So the final sorted table will look like this:
MyTable
Plan Item Sort_Number
-------------------------------------
11018 PS13-2 1
11018 PS3169 2
11018 PSHH278B 3
11026 ZSC1753A 1
11026 ZSC1753B 2
11026 ZSC1753C 3
11026 ZSC1753D 4
11026 ZSO1753A 5
11027 ZSC1754A 1
11027 ZSC1754B 2
Can I do this with SQL? In an MS Access Database.
Thanks in Advance
UPDATE myTable d,
(SELECT Plan, Item,
(SELECT count(*) + 1
FROM myTable i
WHERE i.Plan = o.Plan and i.Item < o.Item) as Sort_Number
FROM myTable o) as s
SET d.Sort_Number = s.Sort_Number
WHERE d.Plan = s.Plan and d.Item = s.Item
OR
UPDATE myTable d INNER JOIN
(SELECT Plan, Item,
(SELECT count(*) + 1
FROM myTable i
WHERE i.Plan = o.Plan and i.Item < o.Item) as Sort_Number
FROM myTable o) s
ON d.Plan = s.Plan and d.Item = s.Item
SET d.Sort_Number = s.Sort_Number
EDIT: I know that this isn't efficient but it will work without any problem for limited number of records.
EDIT2: Please note that I haven't tried this query in MS-Access. I hope it gives you input on how it can be done.
EDIT3: Using a temporary table
SELECT Plan, Item,
(SELECT count(*) + 1
FROM myTable i
WHERE i.Plan = o.Plan and i.Item < o.Item) as Sort_Number
INTO TEMPTABLEFORTEST
FROM myTable o
UPDATE myTable i
INNER JOIN TEMPTABLEFORTEST o
ON i.Plan = o.Plan and i.Item = o.Item set i.Sort_Number = o.Sort_Number
DROP Table TEMPTABLEFORTEST
Creating a SQL statement to update Sort_Number is challenging. However, a VBA procedure for this is fairly simple. I tested this one in Access 2007 with your sample data in a table named "Plans". Substitute your real table name in the "FROM" line below.
Public Sub Update_Sort_Numbers()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSelect As String
Dim lngCurrentPlan As Long
Dim lngSortNum As Long
strSelect = "SELECT p.Plan, p.Item, p.Sort_Number" & vbCrLf & _
"FROM Plans AS p" & vbCrLf & _
"ORDER BY p.Plan, p.Item;"
Set db = CurrentDb
Set rs = db.OpenRecordset(strSelect)
With rs
Do While Not .EOF
If rs!Plan <> lngCurrentPlan Then
lngCurrentPlan = rs!Plan
lngSortNum = 1
End If
Debug.Print !Plan, ![Item], lngSortNum
.Edit
!Sort_Number = lngSortNum
.Update
lngSortNum = lngSortNum + 1
.MoveNext
Loop
.Close
End With
Set rs = Nothing
Set db = Nothing
End Sub

sql select statement against a csv file, using JET.OLEDB driver

I'm trying to execute the following SQL statement (built and tested in SQL Server 2005)
Select *
From mytesttable
where myPk in
(
select t3 from (
select field1, substring(field3, charindex(":", field3),6) t2, min(mypk) t3
from mytesttable
group by field2, substring(field3, charindex(":", field3),6)
) t
)
I know I can't use substring or charindex. So the innermost select looks like this in vbs:
strsql = "select mid(field3, instr(1, field3, ":")), min(mypk) from "
strsql = strsql & myCSVFileName
strsql = strsql & myCSVFileName & " GROUP By mid(field3, instr(1, field3, ":")) "
This runs fine.
But when I try to add the next select to wrap the most inner select, it fails. The code looks like this:
strsql = "select mypk from ( select mid(field3, instr(1, field3, ":")), min(mypk) from "
strsql = strsql & myCSVFileName
strsql = strsql & myCSVFileName & " GROUP By mid(field3, instr(1, field3, ":")) )"
The error message I get is that there is
No value given for one or more required parameters
Any suggestions?
Thanks.
I don't know about Jet, but in SQL Server and other platforms, derived tables must be aliased. In other words, you'd need
... FROM (select ... ) AS YouMustProvideAnAliasForThisDerivedTable
Also be careful to use the same casing each time you mention a column (mypk vs. myPk), though that doesn't seem to be the problem here.
There are a few errors in your string, but providing an alias for a derived table is not necessary in Jet.
You have repeated the line with the csv name, you have used double quotes when single quotes or two double quotes should be used, and you have not got a field called mypk to return. This works for me, but only if field 3 always contains text with a colon.
myCSVFileName = "Table.csv"
strsql = "select pk from ( select mid(field3, instr(1, field3, ':')), min(mypk) as pk from "
strsql = strsql & myCSVFileName & " GROUP By mid(field3, instr(1, field3, ':')) )"