I have a table set up to track when people place an order (Order_Tbl). I duplicated the table that I call my order_change_log and added a date/time field (default value set to now). I have a one to many relationship between the orderID and order_change_logID. The idea is that before update, I want the existing data to be inserted into the change log table. I went into the before update field and made the following statement:
CurrentDB.Execute “INSERT into Order_Change_Log SELECT * FROM Order_Tbl WHERE ChangeLog_ID =“”” & Me.ID & “”””
I keep getting “Invalid Outside Procedure” and I’m getting frustrated… Not sure what I’m doing wrong.
What you want to do is called a trigger. In access a table's triggers can be accessed and created from the table's design mode via [Create Data Macros] on the ribbon. You are pretty much forced to use the macro language to create the trigger.
As an aside it looks like you are setting the default date in Order_Change_Log. Leave that default blank as it is not needed here and in many other cases leads to bugs.
In this case we will be using the after-update macro. The Before-Update values are available using [Old]
For an older example of a trigger happening after Delete see MS Access trigger?
Example Table and Relationships:
After Update Trigger Macro:
Use the ribbon to save the macro and you are done. change some values in the Order_Tbl then refresh or open the Order_Change_Log table to see the results
Related
I have several databases that are used by several applications (one of which is our own, the others we have no control over in what they do).
Out software has to know when the database has last been changed. For reasons I won't get into to keep this short we decided that going with a new table per database that has a singular field: last_changed_on that has a GetDate() as a value. This way our own software can check when it was last changed and check it to the date it has stored for said database and do things if the date is newer than what is stored in-memory.
After doing some research we decided that working with Triggers was the way to go, but from what I could find online, triggers look at specific columns that you set for Updates.
What I'd like to know is if there is a way to automate the process or just have a trigger that happens whenever anything happens insert, update, remove wise?
So I am looking for something like this:
CREATE TRIGGER LastModifiedTrigger
ON [dbo].[anytable]
AFTER INSERT, UPDATE, DELETE
AS
INSERT INTO dbo.LastModifiedTable (last_modified_on) VALUES (CURRENT_TIMESTAMP)
I know that the above example isn't a correct trigger, I'm rather new to them so I was unsure on how to word it.
It might be interesting to note that I can have my own software run several queries creating the queries automatically for each table and each column, but I'd rather avoid to do that as keeping track of all those triggers will be a pain in the long run.
I'd prefer to have a little triggers per database as possible, if only by not having to make a trigger for each individual column name.
Edit: To clarify: I am trying to avoid having to create an automated script that goes and scans every table, and sequentially every column of every table, to create a trigger to see if something is changed there. My biggest issue at the moment is the trigger behavior on updates, but I'm hoping to avoid having to specify tables as well for insert and delete
Edit 2: To avoid future confusion, I'm looking for a solution to this problem for both SQL Server (MS SQL/T SQL) and MySQL
Edit 3: Turns out that I read the documentation very wrongly and (at least on MySql) the trigger activates on any given updated column without having to define a specific one. Regardless, I'm still wondering if there is a way to just have less triggers than having one for each table in a database. (i.e. 1 for any type of update(), 1 for any type of insert(), and 1 for any type of delete()
EDIT 4: Forgot that the argument for overwriting 1 field will come with performance issues, I've considered this and I'm now working with multiple rows. I've also handled the creating of 3 triggers (insert(), update(), and delete()) for each database through my software's code, I really wished this could've been avoided, but it cannot.
Solution
After a bunch more digging on the internet and keep finding opposite results of what I was looking for, and a bunch of trial and error, I found a solution.
First and foremost: having triggers not being dependent on a table (aka, the trigger activates for every table is impossible, it cannot be done, which is too bad, it would've been nice to keep this out of the program code, but nothing I can do about it.
Second: the issue for updates on not being column specific was an error due to my part for searching for triggers not being dependent on specific columns only giving me examples for triggers that are.
The following solution works for MySql, I have yet to test this on SQL Server, but I expect it to not be too different.
CREATE TRIGGER [tablename]_last_modified_insert
AFTER INSERT/UPDATE/DELETE ON [db].[tablename]
FOR EACH ROW
BEGIN
INSERT INTO [db].last_modified(last_modified_on)
VALUES(current_timestamp())
END
As for dynamically creating these triggers, the following show how I get it to work:
First Query:
SHOW TABLES
I run the above query to get all the tables in the database, exclude the last_modified I made myself, and loop through all of them, creating 3 triggers for each.
A big thank you to Arvo and T2PS for their replies, their comments helped by pointing me in the right direction and writing up the solution.
You're slightly off in the assumption that SQL Server triggers are per-column; the CREATE TRIGGER syntax binds the trigger to the named table for the specified operations. The trigger will be called with two logical tables in scope (inserted & deleted) that contain the rows modified by the operation that caused the trigger to fire; if you wanted to check for specific columns' values or changes, then the trigger logic would need to operate against those logical tables.
If you take this approach, you will need to create a trigger for each table you wish to monitor in this fashion; we've had a similar need to track changes (at a more granular level), we didn't find a "pseudotable" that corresponds to all tables in a schema/database. You should also be aware that locking semantics will come into play by doing this, as you will have triggers from multiple tables all targeting the same row for an update as part of separate operations -- depending on the concurrency model in effect, you could be looking at performance consequences by doing so if you expect multiple DML queries to operate concurrently against your database.
I would suggest checking Arvo's commented link above for suitability instead; querying system views is more likely to avoid contention (and other performance-related) issues from using triggers in your scenario.
After a bunch more digging on the internet and keep finding opposite results of what I was looking for, and a bunch of trial and error, I found a solution.
First and foremost: having triggers not being dependent on a table (aka, the trigger activates for every table is impossible, it cannot be done, which is too bad, it would've been nice to keep this out of the program code, but nothing I can do about it.
Second: the issue for updates on not being column specific was an error due to my part for searching for triggers not being dependent on specific columns only giving me examples for triggers that are.
The following solution works for MySQL, I have yet to test this on SQL Server, but I expect it to not be too different.
CREATE TRIGGER [tablename]_last_modified_insert
AFTER INSERT/UPDATE/DELETE ON [db].[tablename]
FOR EACH ROW
BEGIN
INSERT INTO [db].last_modified(last_modified_on)
VALUES(current_timestamp())
END
As for dynamically creating these triggers, the following show how I get it to work:
First Query:
SHOW TABLES
I run the above query to get all the tables in the database, exclude the last_modified I made myself, and loop through all of them, creating 3 triggers for each.
Perhaps you could use Audit for SQL Server:
CREATE SERVER AUDIT [ServerAuditName]
TO FILE
(
FILEPATH = N'C:\Program Files......'
)
ALTER SERVER AUDIT [ServerAuditName] WITH (STATE=ON)
GO
CREATE DATABASE AUDIT SPECIFICATION [mySpec]
FOR SERVER AUDIT [ServerAuditName]
ADD (INSERT, UPDATE, DELETE ON DATABASE::databasename BY [public])
WITH (STATE=ON)
GO
Then you can query for changes:
SELECT *
FROM sys.fn_get_audit_file ('C:\Program Files......',default,default);
GO
Everytime I enter data into one of the SQL linked tables, some of the rows keep saying #deleted no matter what I do. The data is fine in the actual SQL server but not within Access. I never have used Access before so I have no idea what I'm doing. Please use the most non-tech savy dialog as possible... This is all so new to me and I am not good with technology at all.
I have tried refreshing the tables by going to the "Linked Table Manager" thing but that hasn't helped. I tried completely deleting the data from both Access and the SQL Server, re-entering it into the SQL server, and creating a new linked table within Access. I have tried exporting the data into Excel from the server and importing it into Access. None of it has worked. It's only the first 10 rows of data though... The rest of the table is completely fine and all the data has similar structure so I don't know why only the first 10 rows are being affected.
Ok, there are 3 things that often cause this.
1 - Make sure the sql table has a PK column. This is often (useally) a autonumber (incrementing by 1 integer column). So when you create the column in sql server, set it as primry key (a button in the menu can be hit to set PK using the sql manager). Then change in the property sheet the column to identify "yes" and it will set the starting number (1) and the increment (1) for you. Now add the other columns.
So Access needs a PK column.
If above was not your issue, then next up that is common is if you have a "bit" column in sql eerver. These can't be null, or access goes crazy. so if you have a bit column, then MAKE sure you set the default for that in the sql table designer as (0).
If the above don't fix your issue? Then number 3 on the list is to add what is called a row version column to the sql table. Simply add a timestamp column (this is NOT a date column, but is a time stamp row).
In ALL of the above cases, after you make the change to the sql server table, you have to re-link the access table. It is sufficient to right click on the table in Access, choose linked table manager, and then check box the table in queston, and hit ok. The link will be refreshed for you.
So the above are the 3 main issues. In most cases, the PK is the issue. However, if the table on SQL also has a trigger (that inserts) to other tables, then that table trigger has to be changed - but lets take this 1 step and soluion at a time.
As a general rule, Access needs a PK column when working with sql server. If you have that, then check the null "bit" issue - sql server tables need a default setting of 0 for those columns, and if they are null, then Access don't like that.
If both above issues are NOT your issue, then adding a column of timestamp to the sql table will fix this.
Bearings first...
Microsoft Access.
Version? Unsure. For that I see
"Microsoft Office 365 Pro Plus" and
"Access 2007 - 2016 file format"
I'm a MS Access novice, but rather good at relational DBS (Postgres, MySQL, etc...) in which I've created triggers and their companion stored prodecures.
I'm trying to effectively create an after update trigger for a table. I want the trigger to insert a record in a different table with values that I can either echo or customize based on values in the table that was just updated.
In the "table" tab, "after update", this is what I'm defining...
If [Old].[est_mandays]<>[est_mandays] Then
Create a Record In ajax_hist
Alias ah
SetField
Name ah.est_mandays
Value = [Old].[est_mandays]
SetField
Name ah.id
[Old].[id]
End If
As you can see, I'm just echoing those 2 values in the ajax_hist table.
It seems to swallow this OK as far as syntax. At least I don't get any errors. But when I change the value of est_mandays for a record in the table which has the trigger, no record is inserted in the ajax_hist table. No messages of any kind, error, warning or otherwise.
I "saved" the table after the update in an attempt to force the change (just in case). No difference.
Any ideas what I'm doing wrong ?
More importantly, is there a way to debug this (a log file or debug mode or something that tells me the trigger was actually fired ?)
Thanks in Advance!
Get rid of the "[Old]." where setting values.
Thanks to Erik von Asmuth for the tip on USysApplicationLog which gave me the clue I needed.
I have a table tblEmp with a After Insert data macro defined. However, the event is not working. what I am missing here?
Simply move your logic to the BeforeChange trigger which is the state before any record (new or existing) is saved. Your logic attempts to change field values in AfterInsert which is after the save mode. Also, be sure to not include the table identifier, tblEmp, in referencing column name in macro:
In fact, had you clicked the Application Errors on status bar in lower right after your current attempt, the outputted system table indicates the issue since you are in read-only mode after inserting:
EditRecord failed because the default alias represents a record which
is read only.
Ok. Quick background:
MS Access 2003 with 2003/2003 format MDB file upgraded from Access 97.
For the purposes of this example, there are two tables.
Table 1
Asset
ID - (text 20)
ParentID - (text 20)
Other fields
AssetRels
ID - (text 20)
When a record is added to Asset, the ID is added to AssetRels.
From Asset.ID to AssetRels.ID there is a relationship that exists that enforces referential integrity with cascade update and cascade delete.
From AssetRels.ID to Asset.ParentID there is a relationship that enforces referential integrity with cascade update only.
I have 2 records in Asset
VACU0703200, NULL
VACU0703250, VACU0703200
In the data DB, I can go into the table and change VACU0703200 to VACU0704500 and the changes propogate as expected.
If I open the front end DB that links to the data DB, and try the same change (in the table directly) I get "Could not update; currently locked" (no, nothing about 'another session', that's the whole error message)
Both databases are set to "no lock" for the "default record locking"
Obviously, there is some difference in row/page/table level locks that is preventing the cascade update from working.
Does anyone know of some property settings that I can use somewhere to stop this error?
I would prefer not to have to remove the relationship, but otherwise I might have to, and handle it in code.
Edit: Cause is that the table contains a Memo field. Apparently via the linked table, having the Memo field overflow the 4K row size escalates to a table lock. which in turn triggers the problem.
Solution (hackish) is to prevent edits to the ID field on the form, and add a new form to rename. Save changes before opening the new form, and performing the update via an update query works.
I don't see why moving to the front end and editing this from a linked table should make any difference. I would however delete the table links, and then RE link. Often sometimes some properties are set up and read a time of linking, if you go to the back end database and change some of the table properties, often it's a very good idea to RE link the tables.
As a general rule, you more often have to do the above when you add new columns with linked tables to SQL server. However, I would suggest you delete your linked tables in the front end, and try RE linking, this should fix this problem.
Solution (hackish) is to prevent edits to the ID field on the form, and add a new form to rename. Save changes before opening the new form, and performing the update via an update query works.
The cause is a table lock escalation that occurs when the text overflows the 4K table row limit.