I have an Access database that I am trying to insert, update and delete with SQL commands using VBA from an Excel workbook. My update and insert commands work fine but my delete command freezes Excel. I have tried various methods with and without table aliasing.
Set dbConnection = CreateObject("ADODB.Connection") 'New ADODB.Connection
dbConnection.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data source=C:\Users\me\Desktop\Opstats Import\TG_DB.accdb;"
Set dbCommand = CreateObject("ADODB.Command") 'New ADODB.Command
L = "[Excel 12.0 Xml;HDR=YES;IMEX=2;ACCDB=YES;DATABASE=" & ThisWorkbook.FullName & "].[LImport$]"
With dbCommand
.ActiveConnection = dbConnection
'Update Labour
SQL = "UPDATE tblLabour A" & _
" INNER JOIN " & L & " X" & _
" ON A.PositionID = X.PositionID AND A.Date = X.Date AND A.PayCode = X.PayCode" & _
" SET A.Hours = X.Hours, A.Dollars = X.Dollars"
.CommandText = SQL
.Execute
SQL = "DELETE FROM tblLabour" & _
" WHERE NOT EXISTS (SELECT * FROM " & L & " X WHERE X.PositionID = tblLabour.PositionID AND X.Date = tblLabour.Date AND X.PayCode = tblLabour.PayCode)"
.CommandText = SQL
.Execute
'Insert Labour
SQL = "INSERT INTO tblLabour" & _
" SELECT * FROM " & L & " X" & _
" WHERE NOT EXISTS (SELECT PositionID, Date, PayCode FROM tblLabour A WHERE A.PositionID = X.PositionID AND A.Date = X.Date AND A.PayCode = X.PayCode)"
.CommandText = SQL
.Execute
End With
dbConnection.Close
Set dbCommand = Nothing
Set dbConnection = Nothing
I have also tried:
SQL = "DELETE A FROM tblLabour A" & _
" WHERE NOT EXISTS (SELECT * FROM " & L & " X WHERE X.PositionID = A.PositionID AND X.Date = A.Date AND X.PayCode = A.PayCode)"
.CommandText = SQL
.Execute
Because of my data source, I do not have a primary key and rely on three fields to create a composite primary key so the only way to find records is to match all three fields.
I tested the SQL in an Access query and it seems to work fine.
Try concatenating fields to use as a unique identifier with NOT IN().
SQL = "DELETE FROM tblLabour" & _
" WHERE NOT PositionID & PayCode & [Date] IN (SELECT PositionID & PayCode & [Date] FROM " & L & " X)"
Related
As the title suggests, I wondered if it was possible to populate a RecordSource or RowSource without maintaining a persistent connection to the external database I am querying.
I have tried two methods listed below to no avail:
IN Clause
ps.RecordSource = " SELECT TM_Adjudicator AS Adjudicator, Nz(Sum(PS_DB_view.PS_Points+PS_DB_view.PS_Adhoc),0) AS [Total Points] " & _
" FROM SD_Adjudicators_view " & _
" LEFT JOIN PS_DB_view ON (((cstr(SD_Adjudicators_view.TM_Adjudicator) = cstr(PS_DB_view.PS_Adjudicator)) AND cdate(PS_DB_view.PS_Date) >= #" & dtStart_fmt & "#) AND cdate(PS_DB_view.PS_Date) <= #" & dtEnd_fmt & "#) " & _
" IN '' [MS Access;PWD=**STRING**;DATABASE=I:\**PATH**\PM_DB_view.accdb] " & _
" WHERE SD_Adjudicators_view.TM_TeamName = 'Z999' " & _
" GROUP BY TM_Adjudicator; "
(this maintains a persistent connection for the life of the userform
DB.Connection
Set dbs = OpenDatabase("I:\**PATH**\PM_DB_testenvironment.accdb", False, False, "MS Access;**STRING**")
Set rst = dbs.OpenRecordset(" SELECT TM_Adjudicator AS Adjudicator, Nz(Sum(PS_DB_view.PS_Points+PS_DB_view.PS_Adhoc),0) AS [Total Points] " & _
" FROM SD_Adjudicators_view " & _
" LEFT JOIN PS_DB_view ON (((cstr(SD_Adjudicators_view.TM_Adjudicator) = cstr(PS_DB_view.PS_Adjudicator)) AND cdate(PS_DB_view.PS_Date) >= #" & dtStart_fmt & "#) AND cdate(PS_DB_view.PS_Date) <= #" & dtEnd_fmt & "#) " & _
" WHERE SD_Adjudicators_view.TM_TeamName = 'Z999' " & _
" GROUP BY TM_Adjudicator; ")
Set ps.Form.RecordSource = rst
rst.Close
Set rst = Nothing
dbs.Close
Set dbs = Nothing
This too maintains a persistent connection until the close function is called, and the RowSource / RecordSource values are lost.
Is there anyway to read the data, display it, and close the database whilst keeping the records visible?
Yes, there is.
You can use an ADODB disconnected recordset as your record source.
Code:
Dim conn As Object
Set conn = CreateObject("ADODB.Connection")
conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;User ID=Admin;Data Source=I:\**PATH**\PM_DB_testenvironment.accdb;Jet OLEDB:Database Password=""**STRING**"""
Dim rs As Object
Set rs = CreateObject("ADODB.Recordset")
rs.CursorLocation = 3 'adUseClient
rs.Open "SELECT stuff FROM Table", conn, 3, 3 'adOpenStatic, adLockReadOnly
Set Me.Recordset = rs
conn.Close
Set rs.ActiveConnection = Nothing
Set conn = Nothing
Note that many features, like sorting and filtering, won't work unless you first re-establish the database connection.
DAO unfortunately doesn't offer this functionality.
I tested an UPDATE query in Access's query design, and it works, but when I try to use it in my module, I get the error:
Invalid SQL statement; expected... or 'UPDATE'.
My query:
strSql = "UPDATE " & rs.Fields("tableName") & _
" SET " & rs.Fields("foreignKeyName") & " = " & rsContacts.Fields("contactId") & _
" WHERE contactId = " & ContactID
rs: a table that has tableName, foriegnKeyName of the tables to update
rsContacts: a list of contactIds (currently standing on a particular one).
The actual string comes out like this:
UPDATE myTable SET ContactId = 5 WHERE contactId = 2
If the query works, and it is an action query, why am I getting this error?
This is my full code:
Public Sub updateChildTables(ByVal ContactID As Long, ByVal CompanyID As Long)
Dim strSql As String
Dim rs As Recordset
Dim rsPending As Recordset
strSql = "SELECT contactID FROM contacts _
WHERE companyId = " & CompanyID & " and contactId <> " & ContactID
Set rs = CurrentDb.OpenRecordset(strSql)
If Not (rs.BOF And rs.EOF) Then
rs.MoveFirst
strSql = "SELECT * FROM childTables"
Set rsChild = CurrentDb.OpenRecordset(strSql)
rsChild.MoveFirst
Do While Not rsChild.EOF
strSql = "UPDATE " & rsChild.Fields("tableName") & " SET " & rsChild.Fields("foreignKeyName") & " = " & rs.Fields("contactId") & " WHERE contactId = " & ContactID
DoCmd.RunSQL strSql
rs.moveNext
Loop
rsChild.Close
Set rsChild = Nothing
End If
Here is my idea for debugging and possibly even resolving this.
Create a query from within Access normally -- name it UpdateMyTable, for the sake of this example.
Then, rather than using the DoCmd, actually execute this specific query from your VBA.
Dim qry As QueryDef
strSql = "UPDATE " & rsChild.Fields("tableName") & " SET " & _
rsChild.Fields("foreignKeyName") & " = " & _
rs.Fields("contactId") & " WHERE contactId = " & ContactID
Set qry = CurrentDb.QueryDefs("UpdateMyTable")
qry.SQL = strSql
qry.Execute
The big advantage of this is that you can very easily debug this from within Access to both see the rendered SQL and manually run it / tweak it.
Hi all below you see a screenshot of my database:
But now I want to be able to make a table that calculates every players last 5 games. As I'm totaly new to access db I really have no clue how to do this.
Can you guys help me a hand with this one?
When I use the 2nd snippet in the answer below I get these:
Below are SQL routes according to your data. To use in MS Access simply create a new query under Create Tab on Ribbon and place the below SQL in the SQL view of a new created query. You may need to adjust query according to your actual table names and/or fields.
SAME GAMES FOR ALL PLAYERS
Assuming every player shares the same last five games, you could run an aggregate query across all players, using a subquery in INNER JOIN clause to calculate last five game dates. Do note: subquery, LastFiveDates can be saved as its own query and used directly in INNER JOIN.
SELECT [LookUp to Players],
Sum(GamesWon) As SumOfGamesWon, Sum(GamesLost) As SumOfGamesLost,
Sum(OwnOdds) As SumOfOwnOdds, Sum(OppOdds) As SumOfOppOdds,
Sum(GamesPlayed) As SumOfGamesPlayed
FROM GamesTable
INNER JOIN
(
SELECT DISTINCT TOP 5 [Date]
FROM GamesTable
ORDER BY [Date] DESC
) As LastFiveDates
ON GamesTable.[Date] = LastFiveDates.[Date]
GROUP BY [LookUp to Players];
DIFFERING GAMES FOR EACH PLAYER
SIMPLE SELECT APPROACH
Now, if players differ in their last five games, you have to join on different queries or union queries. Again, the below uses a subquery in an inner join but you can save that LastFiveGames as its own stored query and join in INNER JOIN line.
SELECT GamesTable.[LookUp to Players],
Sum(GamesWon) As SumOfGamesWon, Sum(GamesLost) As SumOfGamesLost,
Sum(OwnOdds) As SumOfOwnOdds, Sum(OppOdds) As SumOfOppOdds,
Sum(GamesPlayed) As SumOfGamesPlayed
FROM GamesTable
INNER JOIN
(
SELECT [Lookup to Players], [Date],
(SELECT Count(*)
FROM GamesTable t2
WHERE GamesTable.[Date] <= t2.[Date]
AND GamesTable.[Lookup to Players] = t2.[Lookup to Players]) AS GameOrder
FROM GamesTable
) As LastFiveDates
ON GamesTable.[Date] = LastFiveDates.[Date]
AND GamesTable.[Lookup to Players] = LastFiveDates.[Lookup to Players]
WHERE LastFiveDates.GameOrder <= 5
GROUP BY GamesTable.[LookUp to Players];
DIFFERING GAMES FOR EACH PLAYER
VBA CREATE TABLE APPROACH
Due to performance issues of Access running the query as a stored query, VBA can re-create the GamesStats iteratively looping through all distinct players using the very first query condition for player.
Public Function GameTableStats()
Dim db As Database
Dim tbldef As TableDef, rst As Recordset
Dim strSQL As String, i As Integer
Set db = CurrentDb
Set rst = db.OpenRecordset("SELECT DISTINCT PlayerName FROM GamesTable", dbOpenDynaset)
For Each tbldef In db.TableDefs
If tbldef.Name = "GamesStats" Then
db.Execute "DROP TABLE [GamesStats]", dbFailOnError
End If
Next tbldef
rst.MoveLast
rst.MoveFirst
i = 1
Do While Not rst.EOF
If i = 1 Then
' FIRST PLAYER (MAKE-TABLE QUERY) '
strSQL = "SELECT GamesTable.[PlayerName]," _
& " Sum(GamesWon) As SumOfGamesWon, Sum(GamesLost) As SumOfGamesLost," _
& " Sum(OwnOdds) As SumOfOwnOdds, Sum(OppOdds) As SumOfOppOdds," _
& " Sum(GamePlayed) As SumOfGamePlayed" _
& " INTO GamesStats" _
& " FROM GamesTable" _
& " INNER JOIN" _
& " (" _
& " SELECT DISTINCT TOP 5 [Date], [PlayerName]" _
& " FROM GamesTable" _
& " WHERE [PlayerName]=""" & rst!PlayerName & """" _
& " ORDER BY [Date] DESC" _
& " ) As LastFiveDates" _
& " ON GamesTable.[Date] = LastFiveDates.[Date]" _
& " WHERE GamesTable.[PlayerName]= """ & rst!PlayerName & """" _
& " GROUP BY GamesTable.[PlayerName];"
Else
' ALL OTHER PLAYERS (INSERT APPEND QUERIES) '
strSQL = "INSERT INTO GamesStats ([PlayerName], [SumOfGamesWon], [SumOfGamesLost]," _
& " [SumOfOwnOdds], [SumOfOppOdds], [SumOfGamePlayed])" _
& " SELECT GamesTable.[PlayerName], " _
& " Sum(GamesWon) As SumOfGamesWon, Sum(GamesLost) As SumOfGamesLost, " _
& " Sum(OwnOdds) As SumOfOwnOdds, Sum(OppOdds) As SumOfOppOdds, " _
& " Sum(GamePlayed) As SumOfGamePlayed " _
& " FROM GamesTable " _
& " INNER JOIN " _
& " ( " _
& " SELECT DISTINCT TOP 5 [Date], [PlayerName] " _
& " FROM GamesTable " _
& " WHERE [PlayerName]=""" & rst!PlayerName & """" _
& " ORDER BY [Date] DESC" _
& " ) As LastFiveDates " _
& " ON GamesTable.[Date] = LastFiveDates.[Date]" _
& " WHERE GamesTable.[PlayerName]= """ & rst!PlayerName & """" _
& " GROUP BY GamesTable.[PlayerName];"
End If
db.Execute strSQL, dbFailOnError
i = i + 1
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
Set db = Nothing
MsgBox "Successfully created GamesStats table!", vbInformation
End Function
I am trying to update a single record in sql using a recordset I'm Pulling data from one recordset and trying to save it to another table in the database This is the code I have the insert statement runs fine in SQL.
' Opening the connection
cn.ConnectionString = "Provider=SQLOLEDB; Data Source=" & dbLocation & "; Initial Catalog=Posbdat; User Id=sa"
cn.Open
rh.ConnectionString = "Provider=SQLOLEDB; Data Source=" & dbLocation & "; Initial Catalog=Postrans; User Id=sa"
rh.Open
rs.Open "Select Top 1 * from CustomerPoints order by RedemptionDate desc", cn, adModeReadWrite
x.Open " Select Top 1 * from Register_Header order by datetime desc", rh, adModeReadWrite
rt.Open " SELECT top 1 upc FROM Register_Trans INNER JOIN Register_Header ON Register_Trans.trans_no = Register_Header.trans_no Where trans_subtype = 'AP' Order by Register_Trans.datetime desc ", rh, adOpenDynamic
Debug.Print x!emp_no
Debug.Print x!till_no
Debug.Print x.Fields(10)
Debug.Print itemupc
itemupc = rt.Fields(0)
Dim cmd As New ADODB.Recordset
cmd.Open "UPDATE CustomerPoints set emp_no = " & x!emp_no & _
", till_no = " & x!till_no & " purch_amt = " & x!Total & _
", item_redeem = ' " & itemupc & " ' Where RedemptionDate = (Select top 1 * from CustomerPoints order by " & _
"RedemptionDate Desc)", cn, adOpenDynamic
cmd.update
I haven't closed any of the connections or cleaned it up because it won't run without crashing.
It has been a long time but this is how I used to execute update statements:
Dim conTemp As New ADODB.Connection
conTemp.CommandTimeout = mvarconConnection.CommandTimeout
conTemp.ConnectionTimeout = mvarconConnection.ConnectionTimeout
conTemp.CursorLocation = mvarconConnection.CursorLocation
conTemp.Mode = mvarconConnection.Mode
conTemp.ConnectionString = mvarconConnection.ConnectionString
conTemp.Open mvarconConnection.ConnectionString
conTemp.Execute "SET CONCAT_NULL_YIELDS_NULL OFF"
conTemp.Execute "UPDATE CustomerPoints set emp_no = " & x!emp_no & _
", till_no = " & x!till_no & " purch_amt = " & x!Total & _
", item_redeem = ' " & itemupc & " ' Where RedemptionDate = (Select top 1 * from
CustomerPoints order by " & _
"RedemptionDate Desc)"
The mvarconConnection is just an object that stored all of my DB settings, just replace my settings with your own.
Dim rt As DAO.Recordset
strSQL = "SELECT DISTINCT A.OBJ FROM "
strSQL = strSQL & "(SELECT VARBL AS OBJ FROM AGR_1252 WHERE AGR_NAME = '" _
& AGR & "') A LEFT JOIN "
strSQL = strSQL _
& "(SELECT DISTINCT CONF_USOBT_C_ORG.ORG_OBJECT AS OBJ FROM Role_Content, CONF_USOBT_C_ORG "
strSQL = strSQL & "WHERE Role_Content.AGR_NAME = '" _
& AGR & "' AND Role_Content.TCode = [CONF_USOBT_C_ORG].[Name] AND Role_Content.TCode <> '" & tc & "') B "
strSQL = strSQL & "ON A.OBJ = B.OBJ WHERE B.OBJ Is Null"
Set rt = CurrentDb.OpenRecordset(strSQL)
Do While Not rt.EOF
DoCmd.RunSQL "DELETE FROM AGR_1252 WHERE AGR_NAME = '" & AGR & "' AND VARBL = '" & rt("OBJ") & "'" ', False
rt.MoveNext
Loop
rt.Close
Set rt = Nothing
I have the code above. I dont know why but it's giving me a time out error on the while loop. I dont know if it if because of the Recordset, but the table is blocking after he mades the firts Delete.
There is another way to select records without using RecordSet?
Thanks in advance.
I think you may be looking for something on the lines of:
DELETE FROM AGR_1252
WHERE AGR_NAME = Agr
AND Varbl Not In (
SELECT c.ORG_OBJECT
FROM Role_Content r
INNER JOIN CONF_USOBT_C_ORG c
ON r.TCode =c.[Name]
WHERE r.AGR_NAME Is Null)