I have a tblCustomer and a tblContact, where the information ultimately comes from two different other programs. In order to get around complex ID issues and keep things easy for users, I'm trying to do the following:
If either the CompMyobID or Company (lookup which points to the Primary Key autonumber in tblCustomer) field of a tblContact record are updated, the data macro finds the corresponding tblCustomer record, and uses the Primary Key or the MyobID to update the non-updated field in tblContact.
To achieve that, I've created the following after-update macro - First an If to work out if one of those fields (and which one) has been updated, then an EditRecord, alias not set in order to edit the current record where I SetField the field with the other name to... and that's where I get stuck. I tried to just run some sql, but at the moment nothing actually happens when I update either field...
Edit:
Because, as Erik pointed out, this method locks me out of the current record, and opens me up to recursive issues, Here's another angle that's not yet working:
in a Before Change Data Macro, the following:
You've got two main errors in that data macro:
The just updated record in an after update macro is read-only. This means calling EditRecord will fail. You can work around that by looking up the record you just updated, as shown here, but this requires you
You can't just set the value equal to an SQL clause, and expect the database to execute that clause. Usually, you could lookup records using DLookUp, but data macro's use the Look Up A Record In blocks to look up records instead
The final macro should look something like this (only updating one):
If Updated("CompMyobID") Or Updated("Company") Then
SetLocalVar
NewID
=[ID]
SetLocalVar
NewCompany
=Company
SetLocalVar
NewCompMyobID
=CompMyobID
If Updated("CompMyobID")
Look Up A Record In tblCustomer
Where Condition CompMyobID = NewCompMyobID
Alias T
SetLocalVar
LookupCompany
=T.ID
For Each Record In tblContact
Where Condition ID = NewID
EditRecord
SetField
Company
LookupCompany
End EditRecord
End If
End If
A strong warning: This macro will recursively call itself the way you have designed it! If you update Company, the macro will edit CompMyobID, triggering the macro, then updating Company again, triggering it again, etc.
Access has protection against recursive macro's, so aside from the errors you're triggering, you will probably not notice the recursion, and it won't cause performance problems. I strongly recommend not relying on this protection, and not storing information that could be looked up, but instead using a relation to look up the appropriate company name.
In the end, after the comment discussion below, We used a Before Change data macro, which avoids the need to look up the changed record before altering it, and avoids the chance of the macro calling itself because a Before Update macro edits the current record before it gets updated, instead of updating it again after it gets updated. We changed the macro to check if the value was Null or a zero-length string because Updated is not available in a before update macro.
The final solution was equal to the last edit of the question, with the exception of using Company & "" = "" instead of Company = "" to account for Null values.
You can read more on Null vs zero-length string here. The essence is that a zero-length string is just a string with 0 characters, but Null is a place where no value has been entered. Null behaves a bit oddly, returning Null when you compare it with anything. This means Null = "A" is Null, and gets treated as false, but Null <> "A" is also Null, and also gets treated as false.
Related
Searching this problem returns quite a few search hits, but many off-track answers, so I'm posting a concise description here, and answer below.
The problem afflicts Microsoft Access 2010, and some versions before. Access 2013 renames Memo type to Long Text. I don't know if it has the same problem.
The root problem is associated with running an UPDATE query on a table with a memo field, in certain particular circumstances. This might be an UPDATE query composed in the visual query window, or some VBA running SQL via DAO or ADO or similar. Or it could arise while updating via a form.
(The current post is concerned with this occurrence just within an Access database, though elsewhere you will find discussion of similar-sounding issues when Access is connected to an external database server.)
Instead of generating an immediate and obvious error alert, Access (or perhaps Jet) places the value #Error (which is not just the string "#Error"!) into the Memo field. This might easily go unnoticed until some later time, resulting in visible errors such as:
-- You use Compact and Repair. That seems to complete, but Access quietly adds a MSysCompactError table with a couple of rows. One error -1611 complains that Access was stopped and couldn't complete the operation. A second, more-specific-seeming error complains that it can't find field "Description". That appears to be an internal error that has no relevance.
-- You try to copy the table to another database. Access gives an error complaining that another user is using the table or has updated the table, and won't complete the operation.
-- Other operations on the rows that, unnoticed by you, happen to contain the #Error values fail.
Regardless, the root problem is whatever causes the #Error values to get placed into the Memo fields in the first place.
Many posters have noted that it occurs if the UPDATE attempts to put strings longer than about 2000 characters into the Memo field. That's a surprise, as Memo fields should be able to hold 1 gig characters or more depending on version, even if it only allows 65k through the UI.
So why does the error occur when Updating using >2000 characters?
The key factor that provokes this error is the Memo field having an index. Apparently, although the Memo type field can hold a bazillion characters, the index can't deal with more than about 2000.
Knowing that this is the precipitating factor, probably a number of workarounds come to mind. First, you can obviously just disable the index. This solution is easy to verify in a dummy database: Create two tables containing Memo fields, one with an index and the other without. Run update queries that put >2000 characters into each Memo and note the results.
But perhaps you think you need the index? Your use case might be satisfied if you create a second field that will contain an initial substring of the main Memo (shorter than 2000 characters), and index that instead. This could be used for sorting purposes for example. In most cases, where a memo contains narrative information, it's unlikely that the memo data values differ only after 2000 characters. Or perhaps you can devise a hash function and make a separate column of that.
What if you have a database that already contains these #Error values? Some advice floating around on the web, especially in relation to downstream problems like failure of Compact and Repair, suggests that your database is corrupt and should be abandoned. I'm not so sure. If you can delete the #Error-afflicted rows, then delete the index, and then recreate the deleted rows, you may be back in business. Compact and Repair should run properly at that point, giving some confidence that you fixed the offending part. (Make backups along the way, obviously.)
Workaround solution
Create two macros (Macro1 Macro2)
Macro 1
Get all the necessary information from the open form which includ this long text and close it.
Macro 2
Insert all needed actions (starting with the update query that you get error)
Create a form (Form_on_error) with only a button that run Macro2
Finally add at the end of macro 1
On Error
Go to :Macro Name
Macro Name: On_Error_2590
RunMacro Macro2
Submacro On_error_2590
OpenForm (Form_on_error)
End Submacro
.......and it works !!!
So, only when the update query get error, the user must click the button on the form : Form_on_error
With this Update Query, how can I prevent it from overwriting data in existing rows with blank cells?
UPDATE tbl1
INNER JOIN tbl2
ON tbl1.thing0 = tbl2.thing0
SET tbl2.[thing1] = tbl1.[thing1], tbl2.[thing2] = tbl1.[thing2], tbl2.[thing3] = tbl1.[thing3];
I have users on-site updating a table in real time and worry that remote users updating the table once they have a connection will overwrite the on-site users data with a bunch of blank cells.
Will something like this work if I add it to the end?
WHERE Not Null;
Edit1 for Hansup
Empty in this case means on row1 of the tbl1 where, Onsite User entered data in columns 2 through 7(things redacted in code above). Offsite user has entered nothing in this row today. Offsite user uses the query above to update tbl1. Since columns 2 through 7 are empty on his table, I don't want his work to overwrite Onsite User's edits with blank cells.
Edit2 For HanSup and luk2302
Each row is a project. The projects are created by one user, then multiple users update the projects over their life. So Picture Row 1, thing1, as a customer name that has already been entered. Now, onsite user makes some updates. Offsite user makes none to this row, but then runs the update query.
Offsite user's update should only add info to existing rows, but not remove any data. If it overwrites data with data, I'm fine with that. We are just recording contact info and when certain milestones are reached. So long as the data gets there. I'm not worried about who inputs it.
Excuse formatting, I'm answering from my phone...
You could replace the elements of the SET such that, for example:
SET tbl2.[thing1] = Iif(tbl1.[thing1] is null, tbl2.[thing1], tbl1.[thing2])
In that way, if your new value is null then the other value will be used.
Be aware of possible different behavior of null and empty string, depending on your use case... But you can change the condition to check for empty string too.
I have a feature in my application where a user can opt to UNsubscribe itself. When this happens, there is no change made to the other fields in database and only user's Subscription flag is unchecked. I am doing this by getting a datarow and settingone of the field values and then updating the datarow in the table.
This is working fine for all of the recently created records, however some of the old records have a blank value set for a date field which is now mandatory. Hence when I try to unsubscribe old record of this type, it tries to retrieve null value from the database and then update the same back resulting in an error - "Cannot insert null value in Date Field"
First, if you have a table with a null value in a mandatory column, you would be better served to run an update query on the table that will enter a default value for this mandatory/required column. Wayne G. Dunn mentioned this in the comments to your question. For example:
UPDATE [TABLE1] SET [datefield] = '4/28/1949' where [datefield] = null
**Be sure to backup your data prior to running this kind of query and test with a test table before you execute the query on production data.
Second, if you are doing an update to a row and setting the checkbox value to true, you can review any columns that are required and set their default value in the update query. This makes the query more complex and more difficult to support.
I advise you go with the first option and run an update query on the table. The second option adds unnecessary code to your query that updates the checkbox.
If you post some query examples and table structure, you will get more specific answers. You may consider editing your question to include these details.
I have problems with my records within my database, so I have a template with about 260,000 records and for each record they have 3 identification columns to determine what time period the record is from and location: one for year, one for month, and one for region. Then the information for identifying the specific item is TagName, and Description. The Problem I am having is when someone entered data into this database they entered different description for the same device, I know this because the tag name is the same. Can I write code that will go through the data base find the items with the same tag name and use one of the descriptions to replace the ones that are different to have a more uniform database. Also some devices do not have tag names so we would want to avoid the "" Case.
Also moving forward into the future I have added more columns to the database to allow for more information to be retrieved, is there a way that I can back fill the data to older records once I know that they have the same tag name and Description once the database is cleaned up? Thanks in advance for the information it is much appreciated.
I assume that this will have to be done with VBA of some sort to modify records by looking for the first record with that description and using a variable to assign that description to all the other items with the same tag name? I just am not sure of the correct VBA syntax to go about this. I assume a similar method would be used for the backfilling process?
Your question is rather broad and multifaceted, so I'll answer key parts in steps:
The Problem I am having is when someone entered data into this
database they entered different description for the same device, I
know this because the tag name is the same.
While you could fix up those inconsistencies easily enough with a bit of SQL code, it would be better to avoid those inconsistencies being possible in the first place:
Create a new table, let's call it 'Tags', with TagName and TagDescription fields, and with TagName set as the primary key. Ensure both fields have their Required setting to True and Allow Zero Length to False.
Populate this new table with all possible tags - you can do this with a one-off 'append query' in Access jargon (INSERT INTO statement in SQL).
Delete the tag description column from the main table.
Go into the Relationships view and add a one-to-many relation between the two tables, linking the TagName field in the main table to the TagName field in the Tags table.
As required, create a query that aggregates data from the two tables.
Also some devices do not have tag names so we would want to avoid the
"" Case.
In Access, the concept of an empty string ("") is different from the concept of a true blank or 'null'. As such, it would be a good idea to replace all empty strings (if there are any) with nulls -
UPDATE MyTable SET TagName = Null WHERE TagName = '';
You can then set the TagName field's Allow Zero Length property to False in the table designer.
Also moving forward into the future I have added more columns to the
database to allow for more information to be retrieved
Think less in terms of more columns than more tables.
I assume that this will have to be done with VBA of some sort to modify records
Either VBA, SQL, or the Access query designers (which create SQL code behind the scenes). In terms of being able to crunch through data the quickest, SQL is best, though pure VBA (and in particular, using the DAO object library) can be easier to understand and follow.
hey guys, could someone show me the simple update query through vb? I need to add new fields to the table (just 3) and add a couple text boxes on a form so that users can add some additional data relative to the record (which is already what this form is based on).
So the first form I have is a form that populates a list, when the user double clicks on a selection from that list, it opens a new form, so that the ID of the the table that is tied to this form that I need to add the these text boxes on (all the combo boxes and text boxes relative to one record are tied to the active form at this point, however there are all unbound. On a button click there is already vb that saves the information to the table). I did not create this however, it was built by someone who is not there anymore, and apparently is better than I at this stuff. My problem is that there is soooo much vb that checks for records, and various sql statements based on case, that I cannot decipher it to its simplest form.
So I was looking for a simple example of an update sql statement in vb so I can try to break this apart.
I need it to update the record based on the ID: sql WHERE RecordID = me.RecordID
I actually thought I knew how to do this based on examples, however every time I try, then try to run on button click, I get a run-time error of SYNTAX error, and the debug just highlights the db.execute(sql) part. So I tried to get the resulting immediate window of the sql statement, and it looks fine to me:
UPDATE tblMain
SET [Name] = "John Doe",
[DATE] = #9/30/2009#,
[TYPE] = "TypeA",
WHERE RecordID = 958;
Can I update a table without accounting for every field in the table (because this one has about 15 plus the new 3, so I am ignoring about 14 fields here, but I do not want to alter those anyway???
So as always, I appreciate the help yall!! Thanks!
EDIT:
Sorry I always forget this....I was actaully trying it DAO....
Dim db as DAO.Database
Dim sql as String
set db = CurrentDb
etc
You were thaaat close! You have a simple extra comma after your last column. Get rid of it and it works fine.
UPDATE tblMain SET
[Name] = "John Doe",
[DATE] = #9/30/2009#,
[TYPE] = "TypeA"
WHERE RecordID = 958;
Yes, you can absolutely update only a few columns rather than all of them. That is a best practice, BTW.
Finally, It's considered bad practice to name your columns after reserved words like "Name" and "Date", but I know you inherited this.
You were wise to include Debug.Print sql in your code. bpayne already pointed out the extra comma in your SQL statement.
I want to point out another trouble shooting technique you may find useful to debug SQL statement problems.
Copy the statement from the Immediate Window, and paste it into the SQL View of a new query. Modify the query in the query designer until you can get it working, then revise your VBA code to generate a matching SQL statement.
In this case you might not have noticed the extra comma. However, you could create another new query and build the UPDATE statement from scratch in the query designer. After getting that one working, you could compare its SQL View to the failing query.