Insert values from two different tables into another different table with concatenate - ms-access

I have a button which uses two separate queries that pull data from two tables and insert into another table:
Dim lngID As Long
Dim lngIDCallout As Long
Dim strSQL1 As String
lngID = CalloutAttendance_MultiSelect.Value
lngIDCallout = Forms![Callouts].[CalloutID].Value
strSQL1 = "INSERT INTO Members_Callouts(MemberID) SELECT MemberID FROM Members WHERE MemberID=" & lngID
strSQL2 = "INSERT INTO Members_Callouts(CalloutID) SELECT CalloutID FROM Callouts WHERE CalloutID=" & lngIDCallout
CurrentDb.Execute strSQL1
CurrentDb.Execute strSQL2
CalloutAttendance_MultiSelect.Requery
And whilst it almost does what I want it to do, it inserts the the two values as two separate new records, whereas I'd like it to insert it into ONE new record. I've had a go, but I either get syntax errors, or in the case below, I got a 3067 runtime error "Query input must contain at least one table or query"
strSQL1 = "INSERT INTO Members_Callouts(MemberID, CalloutID) SELECT
(SELECT MemberID FROM Members WHERE MemberID=" & lngID & "),
(SELECT CalloutID FROM Callouts WHERE CalloutID=" & lngIDCallout & ")"
Anyone know where I might be going wrong?
Thanks :-)

In this case you're just inserting the key values so all you need to do is
strSQL1 = _
"INSERT INTO Members_Callouts (MemberID, CalloutID) " & _
"VALUES (" & lngID & ", " & lngIDCallout & ")"
In other words, you don't need to bother with something like...
"(SELECT MemberID FROM Members WHERE MemberID=" & lngID & ")"
...since the value it returns is just lngId (assuming that the value exists in the [Members] table).

INSERT INTO
MyTable (Col1,Col2,Col3,Col4,Col5,Col6,Col7)
SELECT
f1.col1, f2.col2, f3.col3, f3.col4, f3.col5, f4.col6, f5.col7
FROM
(SELECT Col1 FROM Func1()) AS f1
CROSS JOIN
(SELECT Col2 FROM Func2()) AS f2
CROSS JOIN
(SELECT Col3,Col4,Col5 FROM Func3()) AS f3
CROSS JOIN
(SELECT Col6 FROM Func4()) AS f4
CROSS JOIN
(SELECT Col7 FROM Func5()) AS f5

Related

MS Access: Left outer self join with inequality operator

I have a single table in Access 2010:
TMP [CUST,ITEM,START_PD]
I want to get the END_PD for each CUST / ITEM.
END_PD being defined as the period before the next higher START_PD for the same CUST / ITEM.
So I perform a left join of the table to itself, using an inequality operator on START_PD as below.
SELECT s.CUST, s.ITEM, s.START_PD, Min(e.START_PD-1) AS END_PD
FROM TMP AS s
LEFT JOIN TMP AS e ON s.CUST=e.CUST AND s.ITEM=e.ITEM AND e.START_PD>s.START_PD
GROUP BY s.CUST, s.ITEM, s.START_PD
ORDER BY s.CUST, s.ITEM, s.START_PD
The base table has 46,556 rows. I would expect the query result to have the same, but the query only returns 14,967 rows.
Even when I try to return all records in the left join, I get far less than the base table. See below:
SELECT s.*,e.*
FROM TMP AS s
LEFT JOIN TMP AS e ON s.ITEM = e.ITEM AND s.CUST = e.CUST AND e.START_PD>s.START_PD
The above query only returns 19,014 records... less than the base table.
This is holding up a major project, and I'd appreciate any help. So far, it's looking like an Access bug. Any workarounds?
EDIT:
I have tried testing a small sample of the data by including WHERE s.CUST='WALMART' AND s.ITEM='0001H'. This fails by excluding the last START_PD, for which there is none greater.
CUST ITEM START_PD END_PD
WALMART 0001HAC 20694 20696
WALMART 0001HAC 20697 20704
WALMART 0001HAC 20705 20706
Strangely, if I select that same sample of the data (WALMART/0001H) into a separate table and run the EXACT SAME query on that smaller table (changing only the table name), it works fine as below. That is why I tend to think this is a bug.
CUST ITEM START_PD END_PD
WALMART 0001HAC 20694 20696
WALMART 0001HAC 20697 20704
WALMART 0001HAC 20705 20706
WALMART 0001HAC 20707
To answer my own question, I'd like to:
Demonstrate that this is a bug
Provide a workaround
Proof that this is a bug.
Sub Test()
Dim db As DAO.Database, rst As DAO.Recordset
Set db = CurrentDb
On Error Resume Next
db.QueryDefs.Delete "TMP_EXTENDED"
db.QueryDefs.Delete "RAW_LEFT_JOIN"
db.TableDefs.Delete "TMP"
On Error GoTo 0
'Table definition.
strSql = _
"CREATE TABLE TMP ( " & vbCrLf & _
" CUST VARCHAR(10), " & vbCrLf & _
" ITEM VARCHAR(10), " & vbCrLf & _
" START_PD LONG, " & vbCrLf & _
" PRIMARY KEY (CUST,ITEM,START_PD) " & vbCrLf & _
");"
db.Execute strSql, dbFailOnError
'Populate with data.
Set rst = db.OpenRecordset("TMP")
For custNo = 1 To 25 'change to affect final row count
For itemNo = 1 To 100 'change to affect final row count
For pdNo = 1 To 3 'change to affect final row count
strCust = "CUST" & custNo
strItem = "ITEM" & itemNo
rst.AddNew
rst("CUST") = strCust
rst("ITEM") = strItem
rst("START_PD") = pdNo
rst.Update
If rst.RecordCount Mod 1000 = 0 Then Debug.Print rst.RecordCount 'just to monitor.
Next
Next
Next
Debug.Print "TMP Table Row Count is: " & DCount("*", "TMP")
'Test query to find end period for each CUST/ITEM/START_PD.
Dim qdf As New QueryDef
qdf.Name = "TMP_EXTENDED"
qdf.SQL = "SELECT s.CUST, s.ITEM, s.START_PD, Min(e.START_PD-1) AS END_PD " & vbCrLf & _
"FROM TMP AS s " & vbCrLf & _
"LEFT JOIN TMP AS e ON (s.CUST=e.CUST AND s.ITEM=e.ITEM AND e.START_PD>s.START_PD) " & vbCrLf & _
"GROUP BY s.CUST, s.ITEM, s.START_PD " & vbCrLf & _
"ORDER BY s.CUST, s.ITEM, s.START_PD"
db.QueryDefs.Append qdf
Debug.Print "TMP_EXTENDED Row Count is: " & DCount("*", "TMP_EXTENDED")
'Test query to just perform the left join.
Set qdf = New QueryDef
qdf.Name = "RAW_LEFT_JOIN"
qdf.SQL = "SELECT s.*,e.* " & vbCrLf & _
"FROM TMP AS s " & vbCrLf & _
"LEFT JOIN TMP AS e ON s.ITEM = e.ITEM AND s.CUST = e.CUST AND e.START_PD>s.START_PD"
db.QueryDefs.Append qdf
Debug.Print "RAW_LEFT_JOIN Row Count is: " & DCount("*", "RAW_LEFT_JOIN")
RefreshDatabaseWindow
End Sub
When run as written, the above code returns:
TMP Table Row Count is: 7500
TMP_EXTENDED Row Count is: 5000
RAW_LEFT_JOIN Row Count is: 7500
Obviously this is wrong because a left join should always return all the records in the left table. In this case, TMP_EXTENDED should have returned 7500, and RAW_LEFT_JOIN should have returned more than 7500.
The loop bounds for custNo, itemNo, and pdNo can be changed to adjust the record count in table TMP. If you do this, you will see that the query works until the record count reaches about 7000, then it fails.
The same problem does not appear to exist when the join is only performed on one 'entity' column. For example, I modified the above code to use a table with only a CUST column and START_PD column, and got both queries to work properly on a table record count of 600,000.
Workaround
Since posting this, I have found another quite similar post in which a decent workaround is presented. I have modified it as below. I do not know HOW reliable it is going to be, but I'm going with it for now.
SELECT s.CUST, s.ITEM, s.START_PD, Min(e.START_PD) AS END_PD
FROM TMP AS s INNER JOIN TMP AS e ON s.ITEM = e.ITEM AND s.CUST = e.CUST
WHERE e.START_PD>s.START_PD
GROUP BY s.CUST, s.ITEM, s.START_PD
UNION ALL
SELECT CUST, ITEM, Max(START_PD), Null
FROM TMP
GROUP BY CUST, ITEM, Null
ORDER BY CUST,ITEM,START_PD,END_PD
Any "atypical" (not only table1.field = table2.field, but anything with constants, calculations, or comparison operators other than equality) join in Access should have it's ON clause surrounded by braces:
SELECT s.*,e.*
FROM TMP AS s
LEFT JOIN TMP AS e ON (s.ITEM = e.ITEM AND s.CUST = e.CUST AND e.START_PD>s.START_PD)
However, usually, these kinds of joins return a Join expression not supported error instead of giving incorrect results. I don't know why this one doesn't.

MS Access currentdb.excute has different result than Docmd.RunSQL

I use VBA to run an INSERT INTO ... SELECT... SQL query with Currentdb.Execute command. However, I have trouble with the result. There are some rows missing the data.
The result is correct when using Docmd.RunSQL with the same SQL, but I don't want the warning message for inserting data into the table.
I also tried using only the SELECT... with Currentdb.Execute and print the result on the debug window. The result is correct, no missing data.
Here's my code:
strsql = strsql & "INSERT INTO tempLineItems (OrderNo, PositionNo, PartNo, [Description], PlannedDeliveryDate, Qty, Unit, Price, Curr, txta, LineItemText, Discount, Tax) "
strsql = strsql & "SELECT dbo_ttdsls041600.t_orno, dbo_ttdsls041600.t_pono, dbo_ttdsls041600.t_item, IIF(dbo_ttipcs021600.t_dsca is null,dbo_ttiitm001600.t_dsca,dbo_ttipcs021600.t_dsca) AS t_dsca, dbo_ttdsls041600.t_ddta, dbo_ttdsls041600.t_oqua, dbo_ttdsls041600.t_cups, dbo_ttdsls041600.t_pric, dbo_ttdsls040600.t_ccur, dbo_ttdsls041600.t_txta, Null AS LineItemText, dbo_ttdsls041600.t_disc_1, dbo_ttdsls041600.t_cvat "
strsql = strsql & "FROM dbo_ttdsls040600 INNER JOIN ((dbo_ttipcs021600 RIGHT JOIN dbo_ttdsls041600 ON (dbo_ttipcs021600.t_item = dbo_ttdsls041600.t_item) AND (dbo_ttipcs021600.t_cprj = dbo_ttdsls041600.t_cprj)) LEFT JOIN dbo_ttiitm001600 ON dbo_ttdsls041600.t_item = dbo_ttiitm001600.t_item) ON dbo_ttdsls040600.t_orno = dbo_ttdsls041600.t_orno "
strsql = strsql & "WHERE (((dbo_ttdsls041600.t_orno)=" & Me.txtSalesOrderNo.Value & ") AND ((dbo_ttdsls041600.t_pono)>0)) "
strsql = strsql & "ORDER BY dbo_ttdsls041600.t_pono;"
The problem is on the
IIF(dbo_ttipcs021600.t_dsca is null,dbo_ttiitm001600.t_dsca,dbo_ttipcs021600.t_dsca) AS t_dsca
I used ISNULL() function, and it has the same result as using Is Null
3/28/2017 Update:
Those tables are linked tables from the SQL Server. I created local tables based on a subset data of those linked ones, and there's no missing data at all.
Very odd. But I've never had to work much with SQLServer tables. To use RunSQL and not get warnings, turn them off and on.
DoCmd.SetWarnings False
DoCmd.RunSQL ...
DoCmd.SetWarnings True
I'm just curious about this problem, so I did a different approach by using multiple layer of queries.
strsql = strsql & "INSERT INTO tempLineItems (OrderNo, PositionNo, PartNo, Description, PlannedDeliveryDate, Qty, Unit, Price, Curr, txta, LineItemText, Discount, Tax) "
strsql = strsql & "SELECT SO1.t_orno, SO1.t_pono, SO1.t_item, nz(SO1.t_dsca, dbo_ttiitm001600.t_dsca) AS t_dsca, SO1.t_ddta, SO1.t_oqua, SO1.t_cups, SO1.t_pric, SO1.t_ccur, SO1.t_txta, Null AS LineItemText, SO1.t_disc_1, SO1.t_cvat "
strsql = strsql & "FROM (SELECT SO.t_orno, SO.t_ccur, SO.t_pono, SO.t_item, SO.t_oqua, SO.t_pric, SO.t_cups, SO.t_ddta, SO.t_txta, SO.t_disc_1, SO.t_cvat, dbo_ttipcs021600.t_dsca "
strsql = strsql & "FROM (SELECT dbo_ttdsls040600.t_orno, dbo_ttdsls040600.t_ccur, dbo_ttdsls041600.t_pono, dbo_ttdsls041600.t_item, dbo_ttdsls041600.t_pric, dbo_ttdsls041600.t_cups, dbo_ttdsls041600.t_ddta, dbo_ttdsls041600.t_cprj, dbo_ttdsls041600.t_txta, dbo_ttdsls041600.t_disc_1, dbo_ttdsls041600.t_cvat, dbo_ttdsls041600.t_oqua "
strsql = strsql & "FROM dbo_ttdsls041600 INNER JOIN dbo_ttdsls040600 ON dbo_ttdsls041600.t_orno = dbo_ttdsls040600.t_orno "
strsql = strsql & "WHERE (((dbo_ttdsls040600.t_orno)=" & Me.txtSalesOrderNo & "))) SO "
strsql = strsql & "LEFT JOIN dbo_ttipcs021600 ON dbo_ttipcs021600.t_cprj = SO.t_cprj AND dbo_ttipcs021600.t_item = SO.t_item) AS SO1 LEFT JOIN dbo_ttiitm001600 ON dbo_ttiitm001600.t_item = SO1.t_item "
strsql = strsql & "ORDER BY SO1.t_pono;"
And this works!! I guess there's some issue in Access internally.

Using SQL in Query that auto updates

Thank you beforehand for your assistance. I know enough about Access, SQL, and VBA to get myself into trouble. Here is what I want to do.
I want to create a query that starts with a certain year and then lists each year up until the current year. The problem is that I want the query to automatically update as time progresses. In other words, say the start year is 2009, I want my query to list 2009, 2010, 2011, 2012, and 2013 since we are currently in the year 2013. Next year, the list will expand to include 2014. I suspect this is possible using a query in SQL but not sure how to go about coding it properly.
I bet that there is no simple solution for this simple process. We must use VBA to perform following steps:
Create a temporary table:
CREATE Table tblTmpYears (
ID COUNTER CONSTRAINT PrimaryKey PRIMARY KEY,
Year Long
);
In VBA:
Dim strSQL
strSQL = "CREATE Table tblTmpYears (" _
& " ID COUNTER CONSTRAINT PrimaryKey PRIMARY KEY," _
& " Year Long" _
& ");"
CurrentDb.Execute strSQL, dbFailOnError
Fill the temporary table:
INSERT INTO tblTmpYears (year) VALUES (2009);
INSERT INTO tblTmpYears (year) VALUES (2010);
INSERT INTO tblTmpYears (year) VALUES (2011);
INSERT INTO tblTmpYears (year) VALUES (2012);
INSERT INTO tblTmpYears (year) VALUES (2013);
In VBA, for 5 years, valid even after 100 years after our life existence:
Dim y as long, ymin, ymax, strSQL
ymax = Year(Date)
ymin = ymax - 4
For y = ymin to ymax
strSQL = "INSERT INTO tblTmpYears (Year) VALUES (" & y & ");"
CurrentDb.Execute strSQL, dbFailOnError
Next
Create a query for listing with the temporary table:
SELECT * FROM tblStudents INNER JOIN tblTmpYears
ON tblStudents.Year=tblTmpYears.Year
ORDER BY Year;
In VBA like this:
Dim qdf, strSQL
strSQL = "SELECT * FROM tblStudents INNER JOIN tblTmpYears" _
& " ON tblStudents.Year=tblTmpYears.Year" _
& " ORDER BY Year;"
Set qdf = CurrentDB.CreateQueryDef("qrySelTemporary", strSQL)
DoCmd.OpenQuery qdf.Name
Here you will have the Query Datasheet Windows with your students's list, it's printable. Even better, you can use it as
MyReport.RecordSource = "qrySelTemporary"
in an Access Report with a beautiful presentation.
Delete the temporary table after printing, for example:
DROP TABLE tblTmpYears;
In VBA:
Dim strSQL
strSQL = "DROP TABLE tblTmpYears;"
CurrentDb.Execute strSQL, dbFailOnError
Only VBA can accomplish this... rather than a single SQL query.
How about this - a small VBA function that outputs the SQL for an appropriate UNION query, which you can then assign as the RowSource for a combo box, use as a sub-query inside another dynamically generated query, or whatever:
Function CreateYearsToCurrentSQL(From As Integer) As String
Dim I As Integer, S As String
For I = From To Year(Date)
If I <> From Then S = S + "UNION "
S = S + "SELECT " & I & " AS Year FROM MSysObjects" + vbNewLine
Next I
CreateYearsToCurrentSQL = S
End Function
The FROM MSysObjects is because Access will whinge about no FROM clause if one isn't there, and MSysObjects is bound to be an existing table in an Access context (if you prefer though, replace it with the name of any other table).
So I managed to create a Query Criteria that does what I need.
Like (Right(Year(Now()),2)-3) & "-" Or Like (Right(Year(Now()),2)-2) & "-" Or Like (Right(Year(Now()),2)-1) & "-" Or Like Right(Year(Now()),2) & "-"
Thank you everyone for your efforts.

Selecting 2 tables from 2 different databases (ACCESS)

here is the connection i have
strCon="DBQ=" & Server.Mappath("db.mdb") & ";Driver={Microsoft Access Driver (*.mdb)};PWD=password;"
set adoCon=server.createobject("adodb.connection")
adoCon.Open strCon
so in order to work with the 2 databases i have 2 adoCon and when i do the select i select from each db i need
now for the problem...
in this situation i will be able only to get all the info from one and then from the other one. but what i want is to be able to put the together.
db1.tblcats has categories and db2.tblcats has categories and subcategories
so in addition to be able to select both of the together, i need to be able to know what cat is from what db
Step 2 after the big help
this is my code
strSQL = "SELECT name FROM tblcats union " _
& "select name from [MS Access;PWD=pass;DATABASE=" & Server.Mappath("../shop.mdb") & "].tblcats as bcats where bcats.father=50"
rs.CursorType = 3
rs.LockType = 3
rs.Open strSQL, strCon
while not rs.eof
response.write rs("name")&"<br>"
rs.movenext
wend
how can i know what record came from what db? cause i need to act difrently for each one
You can use IN:
SELECT t1.*, t2.*
FROM T1
INNER JOIN
(SELECT * FROM atable
IN 'C:\Docs\DB2.mdb') t2
ON t1.ID=t2.ID
EDIT:
sc = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\docs\other.mdb"
cn.open sc
s="SELECT * FROM t1 INNER JOIN " _
& "[MS Access;PWD=databasePWD;DATABASE=C:\docs\db.mdb].t2 ON t1.ID=t2.ID"
rs.Open s, cn
EDIT 2:
You can use the aliases to identify which database a field is from:
s="SELECT * FROM table1 t INNER JOIN " _
& "[MS Access;PWD=databasePWD;DATABASE=C:\docs\db.mdb].m ON t.ID=m.ID"
msgbox rs.fields("m.code") & " " & rs.fields("t.code")
EDIT 3
Or you can add a virtual field:
SELECT 1 AS "DB", Field, Field FROM ...
UNION ALL
SELECT 2 AS "DB", Field, Field FROM
UNION ALL is usually faster.

checked fields in creating a query

Good day
I have a table(abc) with 2 fields (items = type TEXT) & (Check = type Yes/No)
I created a form, in a sub-form Table(abc) displays items, and a checkbox(check)
How do I code a query to save only the items checked in another table.
plz
It seems that you may want a Make Table or an Append Query:
Dim db As Database
Set db=CurrentDB
''Make table, NewTable must not exist
strSQL="SELECT Field1, Field2, CheckedField INTO NewTable "
& "FROM SomeTable WHERE CheckedField = True"
db.Execute strSQL, dbFailOnError
''Append query
strSQL="INSERT INTO ExistingTable (Field1, Field2, CheckedField) "
& "SELECT Field1, Field2, CheckedField " _
& "FROM SomeTable WHERE CheckedField = True"
db.Execute strSQL, dbFailOnError