Append query with no From clause - sql

I have a MS Access database that records financial transactions. It has a form named frmDeposits that is based on table tblDep. One of the buttons on that form runs a procedure that inserts data from the current record into another table, tblAccount, using DoCmd.RunSql. The code looks like this:
DoCmd.RunSQL "INSERT INTO tblAccount ([Date], CheckNo, Amount, Vendor, Cleared, Deposit, Printed, Misc) " _
& "SELECT [Date], CheckNo, Amount, Vendor, Cleared, " & ident & " AS Deposit, Printed, Misc " & _
& "FROM tblDep WHERE Form=" & formnum & ";"
The formnum variable is set elsewhere, and identifies the current record in the form. The ident variable is also defined elsewhere.
Somehow, in the process of debugging other parts of my code, the last line of this SQL statement was erased, leaving:
DoCmd.RunSQL "INSERT INTO tblAccount ([Date], CheckNo, Amount, Vendor, Cleared, Deposit, Printed, Misc) " _
& "SELECT [Date], CheckNo, Amount, Vendor, Cleared, " & ident & " AS Deposit, Printed, Misc"
Surprisingly, the form continued to operate correctly! This RunSQL statement was still picking up the current record from frmDeposits and inserting it into the tblAccount table.
My question is, why does this work? I thought that SQL statements with a SELECT had to have a FROM clause. I know you can use INSERT INTO ... VALUES with no FROM to insert a single record, but even then you have to supply actual values and not field names.
One other thing I noticed: This only seems to work on code in the form's module. If I paste the SQL string into a query design and run it, or run similar code outside the form's scope, I get the 'Enter Parameter Value' popup box for each field. Is VBA pre-parsing the SQL string, and grabbing the form fields or text boxes for the parameters? Or is it defaulting the FROM to the form's RecordSource? I don't have text boxes on the form for all the fields referenced in the SQL string, although they all exist in the form's underlying table.
Btw, this is running in Access 2000. Yes, I know it's an outdated version, but that's what I have to work with.
**Edit -
One added note, this behavior only shows up when using DoCmd.RunSQL. If I put the same SQL string into a db.Execute, it returns an 'Wrong number of parameters' error.

VBA code exists in two forms in an Access application: plain text source; and compiled "p-code". Perhaps what has happened is the stored p-code didn't get changed when that part of the source code text went missing. IOW, it still includes symbols for the missing FROM clause.
In your situation, I would suspect corruption and perform a decompile to clean up the compiled code. First make a backup of the database for safe keeping. You can find detailed instructions for decompile in the 2 answers to this question: HOW TO decompile and recompile.
Frankly corruption is sort of a wild guess. However, since part of your source code mysteriously disappeared and Access operates as if it's still there, corruption seems like a stronger possibility.
Another possibility is you have DoCmd.SetWarnings = False. Turning SetWarnings off silently discards the error message about the failed INSERT attempt. So you don't see an error message but nothing actually gets inserted, either.

Related

Create and run append queries for multiple linked tables

I am trying to write a VBA code in Microsoft Access that will create and run 60 queries to select new records from 60 linked tables and insert them into 60 tables of the same format. Some background may help here:
I have a large database (lets call the original database "A") that will eventually have over 60 tables, 60 forms & 60 reports. When one of our workers goes out into the field and doesn't have an internet connection, they are going to create new records on a copy of the database stored on their desktop (lets call the duplicate database "B"). Once they have an internet connection, I want them to be able to press a button on either database (I have been trying to code the macro on database A because I thought that would be easiest) and have the new records they created on database B inserted into database A.
I have found code online that seems to be just what I need but when I try to run the macro it gives Error 3022, which says:
The changes you requested to the table were not successful because they would create duplicate values in the index, primary key, or relationship
I have tried running this macro with both databases on my desktop with only 1 linked table with a primary key that is an autonumber, I tried running it with a random autonumber, I tried not having any primary key or index or autonumber and even no records at all. I even tried running it without any linked tables. All ways of trying give me the same Error 3022. I really don't want to create 60 queries one by one so any help would be greatly appreciated. Thank you wizards in advance :)
Here is the code I have tried:
Public Sub ImportTableData(ByVal pstrTable As String, ByVal pstrDb As String)
Dim strSql As String
strSql = "INSERT INTO " & pstrTable & vbNewLine & _
"SELECT *" & vbNewLine & _
"FROM " & pstrTable & " IN '" & pstrDb & "';"
CurrentDb.Execute strSql, dbFailOnError
End Sub
Public Sub ImportAllTables()
Const cstrDb As String = "C:\MyPath\DatabaseB.accdb"
Dim tdf As TableDef
Dim strMsg As String
On Error GoTo ErrorHandler
For Each tdf In CurrentDb.TableDefs
'ignore system and temporary tables '
If Not (tdf.Name Like "MSys*" Or tdf.Name Like "~*") Then
Call ImportTableData(tdf.Name, cstrDb)
End If
Next tdf
ExitHere:
On Error GoTo 0
Set tdf = Nothing
Exit Sub
ErrorHandler:
Select Case Err.Number
Case 3078
strMsg = "Input table " & tdf.Name & " not found."
MsgBox strMsg
Resume Next
Case Else
strMsg = "Error " & Err.Number & " (" & Err.Description & ") in procedure ImportAllTables"
MsgBox strMsg
GoTo ExitHere
End Select
End Sub
When I remove the piece of code: 'dbFailOnError', I get different errors. First thing that pops up when I run the macro is: ''Input table 'LocalTableName' not found'', which is the table I am trying to add the records to. Once I click 'Ok' on that pop up box, that is when Error 3134 pops up which says 'Syntax error in INSERT INTO statement'. I am assuming Error 3134 only pops up because it cannot find the local input table (the fist pop up box).
Also, I tried changing the line of code that says: 'Const cstrDb As String = ''C:\MyPath\DatabaseB.accdb''' to instead point to database A (which is the one I am coding the macro on) like this: 'Const cstrDb As String = ''C:\MyPath\DatabaseA.accdb'''. This doesn't give me the first pop up that says ''Input table 'LocalTableName' not found'' but it still gives Error 3134.
I have no idea what I am doing wrong and have spent over 20 hours on this problem trying dozens of different things. Any help would be greatly appreciated :)
Well, the main issue is how you going to ensure that the PK (primary keys) and FK (foreign keys) remain the same when they go out to the field and start entering data?
There is a good chance that PK/FK values will now be duplicated, or be the same for on one of the field users.
If a user out in the field adds a record, and someone at main work location adds record, they now are to very likly have the same PK value.
I suppose this might work if you use random autonumbers (never even knew that was a option after all these years!!!) - but I can't say even that going to be 100% reliable.
And when you import that copy from the out in the field user, then either:
You always ensure you accept and take the same PK value, or you let access generate a new PK - but if you do that, then the child records FK value would have to be updated then.
You not really try to do a simple import, but are attempting to do a database synchronization- a stunning and VERY advanced concept. And a very challenging problem.
Access (mdb format) did at one time support what is called database replication. This feature would be ideal for your setup.
However, but for newer accDB formats, it not supported anymore. (and quite sure by around access 2010, replication support was dropped anyway).
So, you could try random for the autonumber. I mean, you simple cannot have the PK's being duplicated on each computer - plain and simple.
The other possbile?
You add to each and every table that has a PK, and add a new column called PKF (f for in the field).
And for every table that has a FK, you add a new column called FKF (again, add F to this).
So, in the field, your PK/FK used is NOT the same as the main master database at work.
So, I wrote a Android sync routine based on above. The applcation was Access, but I moved the data to sql server (but same idea). I moved to sql server since my android phone could use its local database (sqlLite) and hit sql server. (but, it would be difficult to get android to hit some server and read + use a accDB file - but was easy to have Android phone hit the sql server database directly).
Gee, maybe they could use Android phones!!! but, this would assume you up to speed writing android software, 100% conformable with SQL server, and also access. I was lucky, had all 3 skill sets, so that is the road and hammer I choose.
And speaking of above?
Maybe your lucky, and you have sql server running at work (not express edition, but full edition). I suggest this considering, since the free edition of SQL express can be what we call a replication subscriber to a main sql database. This allows you to sync your local database with the main mothership database.
So, adopting free SQL server express on each field laptop could be a possible solution. Then when they get to a working network, they sync the database using replication.
But, you could try and roll your own sync system.
I did that for an android applcation I wrote, and for a desktop Access application I had. (but, to make all the moving parts easy, I did adopt sql server for database - continued to use Access as the application/UI part).
Now, using "random" for the PK looks to be a possible solution. I just don't know how random, and if this choice can reliable avoid PK collisions for autonumbers.
Random seems like the best road - but ONLY if that choice would prevent duplicate PK id's being used out in the field for new records.
Edit: Random - not even close - it not random enough
So, a bit of research - no, random PK will not work, you still often wind up with collisions - so that idea is off the table.
I figured it out finally. Basically I changed the line of code that executes the SQL to debug.print. This showed me what was going on. The problem is I had linked the tables from the other database when they didn't need to be linked. The names of the local tables would be 'Table A' & the linked tables were 'Table A1'. So there would be a query generated for 'Table A' and another query generated for 'Table A1'. Since there are no tables by the name 'Table A1' in Database B, the query wouldn't work. Plus the fact that, in the line of code executing the SQL, there was an option that says 'dbFailOnError' so since half of the queries weren't working, this option rolls back any updates made by the queries that did work.
I removed all of the linked tables and the macro runs perfectly, unless there are records that are the same on both databases. If I remove 'dbFailOnError' from the code, then the macro runs well no matter what.
So the macro is doing what I want it to but I would like to keep the 'dbFailOnError' part of the code so I will have to do 2 things. First, I have to solve the problem with the primary keys. The answer on this thread by Albert describes this problem well. Second, I have to adjust the SQL to be able to only select records that don't already exist in Database A. I am assuming I can do this by adding a WHERE to the end of the SQL.
I will make an update once I fix these problems, or if I just run an SQL server instead.
Thank you everyone for your help :)

MS Access: Method to process SQL without using RecordSource

I would like to be able to find the last record in a table easily for SQL INSERT INTO table statement. I was hoping there would be a MS Access object or function which could read a SQL statement without requerying the whole context just to find specific counts or records. As I have been programming the only code I know that reads SQL is a recordset, is there a dummy copy or source you could just read without repointing the record to another? Otherwise, I need a way to access the table in VBA and count all records with a method. If this is not code yet is should be, this would make it so easy to get around code dialects, other methods (unless you need to use form objects) if you know SQL.
I have tried several things, such as that cast a tbl variable but there would be a type mismatch.
This needs a last statement so I get the new record... I need to know how to get the last record in the table with a count.
CurrentDb.Execute "INSERT INTO FormsHelpTable ([ID], [HelpTitle], [Comment]) VALUES " & _
"(" & (lastRecTbl + 1) & ", '" & Me.Text53 & "', '" & Me.Comment & "')", dbFailOnError
Me.RecordSource = "SELECT * FROM FormsHelpTable"
Me.Requery
This worked to find the last record:
http://www.minnesotaithub.com/2013/08/count-records-vba-microsoft-access-2010/
Great code too.

Access vba SQL delete query now stalls and never executes after working for months

I have an access database which has tables linked to it from another access database to hold the data. Daily a lab manager runs a process through a button click which transfers data from this database to a MS SQL Server database and then deletes the data from the Access database. I have inherited these databases and do not have the time or the power to change database structure and we will be replacing the access databases completely with a new LIMS which we are in the process of purchasing and implementing over the next year.
My issue is that one of the DELETE sql queries has stopped executing. Whenever I execute this query the database just hangs up (have let it go as long as 20 minutes) until I pull up the task manager and kill the application. I have tried taking the SQL query out of the VBA environment and running it through a MS Access query and I get the same thing. So basically it worked for a very long time (sense August of last year) and now it has stopped working.
Here is the SQL command for the Delete statement:
strSQL = "DELETE [TableName].* " _
& "FROM [TableName] " _
& "WHERE [TableName].[Run ID] NOT IN (SELECT [Run ID] FROM [SecondTableName] " _
& "WHERE [Run ID] IS NOT NULL)"
Then that sql string is executed through a function in VBA. We use that function dozens if not a hundred times in this procedure and in other spots in the database so I know it is not in that. I also know that the above SQL works because it was working great for over a year and now when I run that statement the database hangs up. There is no invalid data in the table which information is being deleted from, we checked for that first. We also have tried compact and repair on the databases because we are worried this is a corruption in the data database though this does not rule that out and probably is what is going on.
Thank you for your help and let me know if you have questions to help you better understand this issue.
I unfortunately don't have a concrete this is whats wrong answer for you but I'm going to write out how I would approach debugging this. This may be super obvious and you've already tested but I'm a big proponent of double checking even the most trivial of things. The reason to check the SQL even if it is unchanged isn't to check the validity of the SQL but to also identify if nothing has changed on the database end (e.g. large number of new records that makes the nested query too large which causes a timeout, tables being locked etc)
Check the query SELECT [Run ID] FROM [SecondTableName] WHERE [Run ID] IS NOT NULL; and make sure it returns what you expect.
Check the query DELETE [TableName].* "FROM [TableName] WHERE [TableName].[Run ID] IN (<put in a test value>);. This probably involves a couple of test records
Check the query DELETE [TableName].* "FROM [TableName] WHERE [TableName].[Run ID] NOT IN (<put in a test values>);. This probably involves either making a copy of the table and using that or add another conditional in the WHERE statement so you can use test records in your existing table. Since it's in access I would just make the copy unless its prohibitively large.
After all this if you haven't found any issues, then I'm unfortunately out of ideas.

Reference a field on a form within a query using SQL

I have an Access 2007 database that will be housing tables which refer to the bill of materials for multiple products. On the main form I want a user to be able to select one of the products - OK, easy. Now, I want two queries to run once they press a button after choosing their product from a dropdown. The first query is a simple delete query to delete all information on a table. The second query is where I'm having my issue with my SQL syntax. I want the information from a static table to be appended to the table where the delete query just removed everything from.
Now, each table that houses the bill of material for each product is labeled with the product's name. So I want the dropdown (combo0) to be the reference point for the table name in the FROM clause within the SQL string. Code is as follows:
INSERT INTO tblTempSassyInfo (Concat, TableName, AddressName, PartNumber, [L/R], FeederSize, QtyPerBoard, SASSYname, RawBoard)
SELECT TableName & AddressName & PartNumber, TableName, AddressName, PartNumber, [L/R], FeederSize, QtyPerBoard, SassyName, RawBoard
FROM [FORMS]![DASHBOARD]![Combo0];
So you can see where I'm trying to reference the product name in the dropdown on the form as the table name. Please let me know if this is possible.
"... I'm trying to reference the product name in the dropdown on the form as the table name. Please let me know if this is possible."
It is not possible with Access SQL.
The db engine can only accept the actual table name --- it isn't equipped to reference a form control to find the table name nor to accept any other type of parameter to obtain the table name.
You could change the query to include your combo's value as the table name and then rewrite the SQL from the combo's after update event.
"SELECT * FROM [" & [FORMS]![DASHBOARD]![Combo0] & "]"
A similar approach could keep Access happy. But it may not be the best fit for your application.
So, the user essentially wants 2 queries to run. A DELETE * FROM Table query, and an Append query. The user wants to know what table to utilize for the Append query by using the Combobox (may just be my assumption/interpretation). That being said, why not use something along the lines of:
If IsNull(Me.[Combo0].Value) Then
MsgBox "Please select something."
Me.[Combo0].SetFocus
Cancel = True
Else
Select Case Me.Form!Combo0
Case 1
DoCmd.OpenQuery "DeleteMaterialsTableData" 'Query to delete appropriate table data dependent on Combobox selection'
DoCmd.OpenQuery "QueryNameMaterial1" 'Append records to appropriate table dependent on Combo0 selection'
Case 2
DoCmd.OpenQuery "DeleteMaterialsTableData" 'Query to delete appropriate table data dependent on Combobox selection'
DoCmd.OpenQuery "QueryNameMaterial2" 'Append records to appropriate table dependent on Combo0 selection'
This is just trying to use the users' combobox values to determine which table to run the queries against, instead of the user trying to use the Combobox's value as a table name.
You're pressing a button to do this. This implies that some VBA code is running behind the scene (the Click event of the button). In that case, the answer is a resounding Yes.
Dim strSQL as String
Dim strSQL2 as String
strSQL = "DELETE * FROM tblTempSassyInfo;"
DoCmd.RunSQL (strSQL)
strSQL2 = "INSERT INTO tblTempSassyInfo (Concat, TableName, AddressName, PartNumber, [L/R], FeederSize, QtyPerBoard, SASSYname, RawBoard)
SELECT TableName & AddressName & PartNumber, TableName, AddressName, PartNumber, [L/R], FeederSize, QtyPerBoard, SassyName, RawBoard
FROM " & [FORMS]![DASHBOARD]![Combo0].SelectedValue & ";"
DoCmd.RunSQL (strSQL2)
You may need to tweak that a bit, but it should get you pretty close.
You MAY need to use [FORMS]![DASHBOARD]![Combo0].Columns(0) or Columns(1) instead, I can't remember...
As was stated; Access (and just about any brand database) can definitely do append and delete queries.
The problem is the design. Specifically:
FROM [FORMS]![DASHBOARD]![Combo0];
From clause must be a record set (table) not a call to a control on a form.
My suggestion is to first establish a Select query that has the correct data that you want to append. Save that with a name. You need to be able to do this first.
Once that is done - then create an Append query that uses that saved Select query as its starting record set.
You then just need to trigger the Append query (the Select query will automatically run) using vba behind your button click event:
Docmd.OpenQuery "Append Query Name"
This is 100% possible in MS Access 2010 onward based on my experience. I've not used 2007, but MS says it is possible (see link below). I'm using parametrized queries in a few databases.
PARAMETERS [forms].[dash].[dt_val] DateTime;
SELECT a.F3 AS AdEnt, [forms].[dash].[dt_val] AS Expr1...
The important thing I've found is using a form the user will be interacting with and setting the Date as "DateTime" within the parameter. Here is a video from Microsoft that shows how to and says that it applies to 2007.
Use Parameters in MS Access Queries
Additionally, if you want to do a delete or append, save it as a query then place a button on the form that executes the docmd.runquery for the name of that saved delete/append query.

Run Query Against ODBC Connected Table VBA

I have a table (readings) already connected by ODBC in Access that opens very quickly when I click on it.
However, when I try to run this in VBA I it locks up and never displays anything:
Dim strSql As String
strSql = "SELECT readings.ids " & _
"INTO ids_temp " & _
"FROM readings " & _
"WHERE readings.ids > 1234;" //This is id in middle of list
DoCmd.SetWarnings False
DoCmd.RunSQL strSql
DoCmd.SetWarnings True
For some reason this crashes the whole system. Any ideas?
Rather than using DoCmd, t's usually handled by your existing connection to create a Command object, which accepts SQL statements to use with the Command.Execute method.
Reading the documentation for DoCmd, it appears to primarily be intended for eexecuting Macros from the Access UI menus.
Does you Database have ids_temp table locally? If ids_temp table is Linked table it will delete the table, because select into CREATES NEW TABLE. If you want to add to table try INSERT INTO command. You can clean table before inserting the data.
So the error was actually my fault the id I was using was causing the Query to return about 6 million results. But, this method actually works great, I just create the table and link a list box on a different form to the table, then I just show the form. I do some closes and updates in between but overall it seems to work well. Thanks for the help
Let me say that DoCmd.RunSQL is never advisable, particularly with SetWarnings turned OFF, as you have no idea whether the result is what you expect or not. You've told VBA not to report errors, so you never know if all the records were inserted or not.
It's very easy to replace DoCmd.RunSQL with my SQLRun() function, posted here:
How can I get a value from the update query prompt in Access VBA?