Creating and using a table in the same transaction - sql

I am trying to run a certain module in ms access. This is the part where I start running into problems:
For i= asc("A") To asc("C")
DBEngine.Workspaces(0).BeginTrans
'...a lot more code...
strSql = "SELECT table.string, Count(*) INTO tableCount FROM table LEFT JOIN table2 ON table.string= table2.string WHERE table2.string Is Null AND (((table.string) Like [param])) GROUP BY table.string HAVING (((Count(*))>1))"
DoCmd.SetWarnings False
Set qdf = CurrentDb.CreateQueryDef("", strSql)
qdf.Parameters("param").Value = Chr(i) & "*"
Call qdf.Execute(dbFailOnError)
DoCmd.SetWarnings True
If DCount("*", "tableCount") = 0 Then 'check if there were records
GoTo nextIteration
End If
'...a lot more code using tableCount if DCount shows that there are records...
DBEngine.Workspaces(0).CommitTrans
Next i
When arriving at the DCount check I get an error :
The Microsoft Access engine cannot find the input table or query 'tableCount'...
And the table doesn't appear in the db. I tried running the query that is in strSql in the Create Query in Access. When asked for param I enter A* and it works fine. The table tableCount is not created in the database before the transaction commit, But I need it inside the transaction, and not only for DCount. Is there a way to use the table or it's data before committing?
Please ask for any additional information you need in order to answer me, in comments. I will respond right away.

Use the following it is simpler and keeps everything within the transaction:
Dim dbS As DAO.Database
Dim wsP As DAO.Workspace
Dim lngRecs as Long
Set wsP = DBEngine.Workspaces(0)
Set dbS = CurrentDb
strSql = "SELECT table.string, Count(*) INTO tableCount FROM table LEFT JOIN table2 ON table.string= table2.string WHERE table2.string Is Null AND (((table.string) Like '" & Chr(i) & "*'")) GROUP BY table.string HAVING (((Count(*))>1))"
wsP.BeginTrans
dbS.Execute strSql, dbFailOnError
lngRecs = dbS.RecordsAffected
If Not lngRecs > 0 Then
GoTo nextIteration
End If
'Do whatever you like with lngRecs
wsP.CommitTrans dbForceOSFlush

Related

How to use DAO.Recordset to update a table using a non-Updateable Query

I have a fairly small table tblFunding with 20 records. I want to update its field Payment using results of a non-updateable query ie:
"SELECT UserName, Sum([Payment]) As Payment FROM tblFundingMain WHERE (((DateDiff('m',[PaymentDate],DateSerial(Year(Date()),1,1))) Between -7 And 4)) GROUP BY UserName")
I know it is bad practice to store this type of data but the user wants to have a glance at the data from time to time since the table is bound to a form for his convenience.
We came out with a method that uses DAO which works but it leaves nulls for records that do not exist for UserName field in the updated table i.e tblFunding. We would prefer the value to be 0 in case the fields do not match. The code runs before the form opens which means the table is updated before the form is launched. Is there a way to politely accomplish this task? Please see the code below and advise wherever you can. Thank you!
Private Sub btnGlance_Click()
Dim rs1 As DAO.Recordset
Dim rs 2 As DAO.Recordset
Set rs1 = CurrentDb.OpenRecordset("SELECT UserName, Sum([Payment]) As Payment FROM tblFundingMain WHERE (((DateDiff('m',[PaymentDate],DateSerial(Year(Date()),1,1))) Between -7 And 4)) GROUP BY UserName")
Set rs2 = CurrentDb.OpenRecordset("SELECT * FROM tblFunding")
rs1.MoveFirst
Do Until rs1.EOF
rs2.MoveFirst
Do Until rs2.EOF
If rs1.Fields("UserName") = rs2.Fields("UserName") Then
rs2.Edit
rs2.Fields("Payment").Value = rs1.Fields("Payment").Value
rs2.Update
End If
rs2.MoveNext
Loop
rs1.MoveNext
Loop
rs1.Close
rs2.Close
Set rs1 = Nothing
Set rs2 = Nothing
Docmd.OpenForm "frmUserGlance"
End Sub
Could run an UPDATE action to change all Null to 0.
CurrentDb.Execute "UPDATE tblFunding SET Payment = 0 WHERE Payment Is Null"
Or consider alternate code:
rs2.MoveFirst
Do Until rs2.EOF
rs1.MoveFirst
rs1.FindFirst "UserName = '" & rs2!UserName & "'"
rs2.Edit
If Not rs1.NoMatch Then
rs2!Payment = rs1!Payment
Else
rs2!Payment = 0
End If
rs2.Update
rs2.MoveNext
Loop
Alternative to display this summary data on form could use domain aggregate function. Build a query object that does the summation then use DLookup to pull a particular value. Or use DSum() directly on source table.
Could avoid all this code if just did a query that JOINED filtered summation query to dataset of all user names.

How to fix runtime error '3048' "Cannot open any more databases."

I have a form with a button that calls and filters a couple of union queries with about 40 SELECT queries total in between them. It then displays the data in a report. Each SELECT query in the Union query collects records from multiple unique tables in the database. I recently had to add a couple more SELECT queries into the union query to grab records from new tables which is when I got the runtime error. It was opening the report fine before I added these SELECT queries so im under the assumption is there are too many SELECT queries in the UNION query. To resolve this issue, do I simply not use a UNION query and find an alternative way to combine records? or is it something in the VBA code that needs adjustment?
Here is my code
Private Sub Command189_Click()
DoCmd.SetWarnings False
DoCmd.Close acReport, "Operator Daily Review"
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim varItem As Variant
Dim strCriteria As String
Dim strSQL As String
Set db = CurrentDb()
Set qdf = db.QueryDefs("Productivity_WeeklyFinal")
Set qdf2 = db.QueryDefs("qFiller_Names")
strSQL = "SELECT Info_ME_Employees.ID, gs_1_week_finalUnion.SampleID,
gs_1_week_finalUnion.Operator, Format$([TestDate],'m/dd/yyyy') AS Test_Date,
gs_1_week_finalUnion.Test FROM Info_ME_Employees INNER JOIN gs_1_week_finalUnion ON
Info_ME_Employees.Full_Name = gs_1_week_finalUnion.Operator" & _
" WHERE Info_ME_Employees.ID IN (4,5,6,7)AND gs_1_week_finalUnion.TestDate Between (Date()-7-
Weekday(Date(),2)) And (Date()-Weekday(Date(),2)-1) " & _
" ORDER BY gs_1_week_finalUnion.Operator"
strSQL2 = "SELECT Info_ME_Employees.ID, Info_ME_Employees.Full_Name FROM Info_ME_Employees" & _
" WHERE Info_ME_Employees.ID IN (4,5,6,7)"
qdf.SQL = strSQL
qdf2.SQL = strSQL2
DoCmd.OpenReport "Operator Daily Review", acViewReport
Set db = Nothing
Set qdf = Nothing
End Sub
I think that there is a limit of tables that can be included in a UNION query - possibly 32. Therefore your options are:
Create several UNION queries, and then UNION them all together as the final step;
Insert the data into a temp table using each individual part of the union query.
Additionally, there may be some way that your database could be re-designed, as it is quite unusual to have to have some many unions needed.
Regards,
Actually, the statement for this "error" is incorrect!
“Cannot open any more databases.” What microsoft should have said here is that no more links to a database can be opened. That is why adding more UNIONs caused this error. Because each separate reference to a link to an object (table or query) causes another link (microsoft uses the term "database") to be opened.

Index and match a field name based off another table in Access SQL code

I am using access to update some data values. I need to associate a number 1 to 50 to a numeric value associated with a trait from another table. The problem I am having is I need to index and match the two tables to match the two values then index the trait associated with the value. I can do this in Microsoft Excel with the following code...I would like to automate this process in access with a button but not sure how to index and match in Access.
=INDEX(B:B,MATCH(K:K,A:A))
In Access I've tried using an update query but index the trait value with the associated value is difficult without going into VBA code. The shd-50 and slk_50 have matching values to the Days after July 1st. I would like to update these values with the Daily HU value which are equivalent to Days after July 1st field. The GDUs_afterJuly1st are fixed values ranging from 1-50 and have a trait assigned to each number so 1=551, 2=638 ... 49 = 1440. While the tblFieldBook table I want to update the GDU_shd50 and GDU_slk50 with the 551, 638, and 1440.
So the Yellow rows mean the GDUs_afterJuly1st and the Orange rows are the tblFieldBook. I would like to update the GDU_shd50 and GDU_slk50.
I found this code was able to get the data I need. I just have some renaming that I would like to do of field names and table names. I will post this in the code when I figure that out.
Option Compare Database
Option Explicit
Private Sub Calculate_GDUs_Click()
Dim StrSql As String
Dim db As Database
Dim fld As Object
DoCmd.SetWarnings False
'run query to match and index GDUs to days after july
StrSql = "SELECT * INTO GDU_slk_50 FROM tblFieldBook LEFT JOIN GDUs_afterJuly1st ON tblFieldBook.[slk_50] = GDUs_afterJuly1st.[Days after July 1st]"
DoCmd.RunSQL (StrSql)
StrSql = "SELECT * INTO GDU_shd_50 FROM tblFieldBook LEFT JOIN GDUs_afterJuly1st ON tblFieldBook.[shd_50] = GDUs_afterJuly1st.[Days after July 1st]"
DoCmd.RunSQL (StrSql)
StrSql = "SELECT GDU_shd_50.[Daily HU], tblFieldBook.* INTO [tblFieldBookwshd50] FROM GDU_shd_50 INNER JOIN tblFieldBook ON GDU_shd_50.recID = tblFieldBook.recID"
DoCmd.RunSQL (StrSql)
StrSql = "SELECT GDU_slk_50.[Daily HU], [tblFieldBookwshd50].* INTO [tblSlkShdGDUs] FROM GDU_slk_50 INNER JOIN [tblFieldBookwshd50] ON GDU_slk_50.recID = [tblFieldBookwshd50].recID"
DoCmd.RunSQL (StrSql)
StrSql = "DELETE tblSlkShdGDUs.[GDU_slk_50_Daily HU] FROM tblSlkShdGDUs WHERE (((tblSlkShdGDUs.[GDU_slk_50_Daily HU]) Is Null))"
DoCmd.RunSQL (StrSql)
'delete tables not needed anymore
DoCmd.DeleteObject acTable, "GDU_shd_50"
DoCmd.DeleteObject acTable, "GDU_slk_50"
DoCmd.DeleteObject acTable, "tblFieldBookwshd50"
'rename columns
Set db = CurrentDb
Set fld = db.TableDefs("tblSlkShdGDUs").Fields("GDU_slk_50_Daily HU")
fld.Name = "GDU_slk50"
Set fld = db.TableDefs("tblSlkShdGDUs").Fields("tblFieldBookwshd50_Daily HU")
fld.Name = "GDU_shd50"
Set db = Nothing
Set fld = Nothing
'run export macro
xlsxExport
DoCmd.SetWarnings True
End Sub

Locking record on MS ACCESS append/insert [duplicate]

I am using an MS Access append query to append inventory transactions to my ERP database (MYSQL).
Please advise how I would go about to modify my query to automatically insert the next sequential transaction ID (primary key) into the Inventory_transaction table, with ability to append multiple records at once.
My existing query works fine, but only when I append just one record.
I usually need to append multiple records simultaneously. Each record needs to have a unique sequential transaction ID (primary key). There would be multiple users using this app simultaneously, so I need minimal chance of duplicate a key violation, to prevent roll backs. I tried appending without using a primary key to see if my database would automatically assign a transaction ID, but unfortunately this this ERP field is not an auto-number and I cant modify the table structure...
Below are 2 queries.
This one currently works for generating a transaction ID for just one record.
SELECT Max([SYSADM_INVENTORY_TRANS].[TRANSACTION_ID])+1 AS new_inventory_transaction_ID
FROM SYSADM_INVENTORY_TRANS;
The 2nd query is the append query that contains the first query and I would much appreciate it if someone can modify the query so the user has ability to append multiple records at once with a unique transaction ID.
INSERT INTO SYSADM_INVENTORY_TRANS ( TRANSACTION_ID, WORKORDER_TYPE,
WORKORDER_BASE_ID, WORKORDER_LOT_ID, WORKORDER_SPLIT_ID, WORKORDER_SUB_ID,
OPERATION_SEQ_NO, REQ_PIECE_NO, PART_ID, TYPE, CLASS, QTY, COSTED_QTY,
TRANSACTION_DATE, WAREHOUSE_ID, LOCATION_ID, USER_ID, POSTING_CANDIDATE,
ACT_MATERIAL_COST, ACT_LABOR_COST, ACT_BURDEN_COST, ACT_SERVICE_COST,
CREATE_DATE, ADD_BURDEN, COUNT_SEQUENCE, DESCRIPTION )
SELECT T.new_inventory_transaction_ID, S.WORKORDER_TYPE, D.WORKORDER_BASE_ID,
D.WORKORDER_LOT_ID, D.WORKORDER_SPLIT_ID, D.WORKORDER_SUB_ID, D.OPERATION_SEQ_NO,
D.PIECE_NO, D.auto_issue_part_ID, S.TYPE, S.CLASS, D.[total_auto_issue Qty],
0 AS Expr6, Date() AS Expr1, D.BACKFLUSH_WHS_ID, D.BACKFLUSH_LOC_ID,
"SYSADM" AS Expr3, S.POSTING_CANDIDATE, S.ACT_MATERIAL_COST, S.ACT_LABOR_COST,
S.ACT_BURDEN_COST, S.ACT_SERVICE_COST, Date() AS Expr2, S.ADD_BURDEN,
S.COUNT_SEQUENCE, "ENTERED WITH ACCESS APP" AS Expr5
FROM tbl_static_autoissue_data AS S,
tbl_dynamic_autoissue_data AS D,
qry_transaction_ID_generator AS T;
Here are some notes that may help you towards your goal, however life would be a lot easier and a lot safer with autonumbers. This is VBA as you mention MS Access.
Function NextTranNumber(ByRef FirstTran As Long, _
ByRef LastTran As Long, Optional BlockSize = 1)
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strSQL As String
Dim lngResult As Long
Dim strCon As String
lngResult = 0 'assume fail
strCon = TestCon ''Connection to back-end
cn.Open strCon
rs.CursorType = adOpenKeyset
rs.LockType = adLockPessimistic
rs.CursorLocation = adUseServer
''Where BEInfo is a single line table that holds a transaction seed
strSQL = "SELECT ASeqNumber FROM BEInfo"
rs.Open strSQL, cn, , , adCmdText
'Note this is ADO, so no rs.Edit
FirstTran = rs!ASeqNumber + 1
rs!ASeqNumber = rs!ASeqNumber + BlockSize
rs.Update
LastTran = rs!ASeqNumber
rs.Close
Set rs = Nothing
End Function
Sub TransactionProcessing()
Dim FirstTran As Long
Dim LastTran As Long
Dim db As Database
Dim sSQL As String
Dim Block As Long
Dim rs As DAO.Recordset
Set db = CurrentDb
'Existing temporary table
sSQL = "DELETE FROM FETempTrans"
db.Execute sSQL, dbFailOnError
'The records to be added to the main table
sSQL = "INSERT INTO FETempTrans ( ID, AText ) SELECT 0 AS ID, AText FROM Table1"
db.Execute sSQL, dbFailOnError
Block = db.RecordsAffected
'Reserve a transaction block based on the temp table count
NextTranNumber FirstTran, LastTran, Block
Set rs = db.OpenRecordset("FETempTrans")
Do While Not rs.EOF
rs.Edit
rs!ID = FirstTran
rs.Update
FirstTran = FirstTran + 1
rs.MoveNext
Loop
If FirstTran - 1 = LastTran Then
'compare the temp set to the main table
'if it passes, update the main table
Else
'fail
End If
End Sub

Adding a new record with VBA

I have a form in which one of the ComboBoxes lists all the documents of a given project. The user should select one and after pressing a button, and if present in Table Dessinsit opens a second form showing that record. If it is not present in that table, I want to add it in.
One of my collegues told me all I had to do was to execute an SQL query with VBA. What I have so far is this:
Dim rsDessin As DAO.Recordset
Dim strContrat As String
Dim strProjet As String
Dim strDessin As String
Dim sqlquery As String
'I think these next 3 lines are unimportant. I set a first query to get information I need from another table
strDessin = Me.Combo_Dessin
strProjet = Me.Combo_Projet
sqlquery = "SELECT [Projet HNA] FROM [Projets] WHERE [Projet AHNS] = '" & strProjet & "'"
Set rsDessin = CurrentDb.OpenRecordset(sqlquery)
If Not rsDessin.RecordCount > 0 Then 'If not present I want to add it
strContrat = rsDessin![Projet HNA]
sqlquery = "INSERT INTO Feuilles ([AHNS], [Contrat], [No Projet]) VALUES (strDessin, strContrat, strDessin)"
'Not sure what to do with this query or how to make sure it worked.
End If
'Checking my variables
Debug.Print strProjet
Debug.Print strContrat
Debug.Print strDessin
'By here I'd like to have inserted my new record.
rsDessin.Close
Set rsDessin = Nothing
I also read online that i could achieve a similar result with something like this:
Set R = CurrentDb.OpenRecordset("SELECT * FROM [Dessins]")
R.AddNew
R![Contrat] = strContrat
R![Projet] = strProjet
R![AHNS] = strDessin
R.Update
R.Close
Set R = Nothing
DoCmd.Close
Is one way better than the other? In the case where my INSERT INTO query is better, what should I do to execute it?
You're asking which is preferable when inserting a record: to use an SQL statement issued to the Database object, or to use the methods of the Recordset object.
For a single record, it doesn't matter. However, you could issue the INSERT statement like this:
CurrentDb.Execute "INSERT INTO Feuilles ([AHNS], [Contrat], [No Projet]) VALUES (" & strDessin & ", " & strContrat & ", " & strDessin & ")", dbFailOnError
(You should use the dbFailOnError option to catch certain errors, as HansUp points out in this answer.)
For inserting multiple records from another table or query, it is generally faster and more efficient to issue an SQL statement like this:
Dim sql = _
"INSERT INTO DestinationTable (Field1, Field2, Field3) " & _
"SELECT Field1, Field2, Field3 " & _
"FROM SourceTable"
CurrentDb.Execute sql
than the equivalent using the Recordset object:
Dim rsSource As DAO.Recordset, rsDestination As DAO.Recordset
Set rsSource = CurrentDb.OpenRecordset("SourceTable")
Set rsDestination = CurrentDb.OpenRecordset("DestinationTable")
Do Until rs.EOF
rsDestination.AddNew
rsDestination!Field1 = rsSource!Field1
rsDestination!Field2 = rsSource!Field2
rsDestination!Field3 = rsSource!Field3
rsDestination.Update
rs.MoveNext
Loop
That said, using an SQL statement has its limitations:
You are limited to SQL syntax and functions.
This is partially mitigated in Access, because SQL statements can use many VBA built-in functions or functions that you define.
SQL statements are designed to work on blocks of rows. Per-row logic is harder to express using only the Iif, Choose, or Switch functions; and logic that depends on the current state (e.g. insert every other record) is harder or impossible using pure SQL. This can be easily done using the Recordset methods approach.
This too can be enabled using a combination of VBA and SQL, if you have functions that persist state in module-level variables. One caveat: you'll need to reset the state each time before issuing the SQL statement. See here for an example.
One part* of your question asked about INSERT vs. Recordset.AddNew to add one row. I suggest this recordset approach:
Dim db As DAO.Database
Dim R As DAO.Recordset
Set db = CurrentDb
Set R = db.OpenRecordset("Dessins", dbOpenTable, dbAppendOnly)
With R
.AddNew
!Contrat = rsDessin![Projet HNA].Value
!Projet = Me.Combo_Projet.Value
!AHNS = Me.Combo_Dessin.Value
.Update
.Close
End With
* You also asked how to execute an INSERT. Use the DAO.Database.Execute method which Zev recommended and include the dbFailOnError option. That will add clarity about certain insert failures. For example, a key violation error could otherwise make your INSERT fail silently. But including dbFailOnError ensures you get notified about the problem immediately. So always include that option ... except in cases where you actually want to allow an INSERT to fail silently. (For me, that's never.)