Tray2.se Logo

Using table triggers to log changes in your database tables.

Tracking changes in a table or several tables for that matter can be essential for your application, and we are going to take a look on how to do that using only the MySQL/MariaDB database. The examples will be using Laravel, but the syntax for creating tiggers is purely SQL so it is language and framework agnostic.

We have been tasked to log all the changes made to an orders table in an e-commerce application built with Laravel, and we need to track these events.

  1. Order creation
  2. Packing started
  3. Packing finished
  4. Prepared for shipping
  5. Shipped
  6. Received

One way to do this would be to use Event(s) in Laravel to track all these changes, but I think a better solution would be to let the database do all the heavy lifting. What I mean by that is that instead of registering an event that we fire each time we do any of the above, we let the database trigger those events instead. We will be using something that is called Triggers or Table triggers, you can register one or more triggers on a table that does different things depending on the action being performed on the table that has the trigger.

The actions we can tell the table to trigger on are INSERT, UPDATE and DELETE, we can also specify when we should trigger the event, BEFORE or AFTER the action. The before can manipulate the data being inserted or updated, before it is stored in the table, and the after allows us to do something after it has been stored in the table. We will be using AFTER since we are talking about logging changes made to a table.

The syntax to create a trigger is

1CREATE TRIGGER <trigger_name> <BEFORE|AFTER> <ACTION>
2 ON <table_name> FOR EACH ROW
3BEGIN
4 /* What the trigger should do */
5END;

In MySQL 8 you can create a multiple action trigger in one go, in earlier versions you need to specify them one by one.

So let's start with the trigger for when an order is created (inserted).

1CREATE TRIGGER insert_order_trigger AFTER INSERT
2 ON orders FOR EACH ROW
3BEGIN
4 INSERT INTO order_logs(action, order_id, old_status, new_status, created_at, updated_at)
5 VALUES('INSERT', NEW.id, NULL, NEW.status, NOW(), NOW());
6END;

The values are stored in a variable called NEW, and from that variable we can get all the columns that was inserted into the orders table, the values are accessed with the dot syntax. For simplicity’s sake, we are just storing the id and the status of order and when it happened. We are also storing the previous status, so that we can see if one of the steps has been skipped, or if they needed to start over with the order. The old value for an insert is null.

So let's move on to the update action, the syntax will be almost the same.

1CREATE TRIGGER update_order_trigger AFTER UPDATE
2 ON orders FOR EACH ROW
3BEGIN
4 INSERT INTO order_logs(action, order_id, old_status, new_status, created_at, updated_at)
5 VALUES('UPDATE', NEW.id, OLD.status, NEW.status, NOW(), NOW());
6END;

The difference other than the name of the trigger and the action, is that we use the OLD variable to get the status of the order before we did the update, that way we will see the order going from status created to packing started.

However before we can do anything with the trigger code, we need to create the orders table. We don't really care about anything more since this is just a demonstration of how triggers work.

1public function up(): void
2{
3 Schema::create('orders', function (Blueprint $table) {
4 $table->id();
5 $table->integer('status');
6 $table->foreignIdFor(User::class);
7 $table->timestamps();
8 });
9}

The code above creates our orders table, so now we can add the triggers, and we do that in the up method of our migration, and since there is no native support in Laravel for creating triggers, we will have to use the DB facade to create them. To keep this brief I will also create the order_logs table in the same migration. It is very important that both orders and order_logs exists before the triggers are created.

1public function up(): void
2{
3 Schema::create('orders', function (Blueprint $table) {
4 $table->id();
5 $table->integer('status');
6 $table->foreignIdFor(User::class);
7 $table->timestamps();
8 });
9 
10 Schema::create('order_logs', function (Blueprint $table) {
11 $table->id();
12 $table->unsignedBigInteger('order_id');
13 $table->string('action');
14 $table->integer('old_status')->nullable();
15 $table->integer('new_status');
16 $table->timestamps();
17 });
18 
19 DB::statement("CREATE TRIGGER insert_order_trigger AFTER INSERT
20 ON orders FOR EACH ROW
21 BEGIN
22 INSERT INTO order_logs(action, order_id, old_status, new_status, created_at, updated_at)
23 VALUES('INSERT', NEW.id, NULL, NEW.status, NOW(), NOW());
24 END;
25 ");
26 DB::statement("CREATE TRIGGER update_order_trigger AFTER UPDATE
27 ON orders FOR EACH ROW
28 BEGIN
29 INSERT INTO order_logs(action, order_id, old_status, new_status, created_at, updated_at)
30 VALUES('UPDATE', NEW.id, OLD.status, NEW.status, NOW(), NOW());
31 END;
32 ");
33}

Before we attempt to run our migration we need to take a look at the down method in our migration file, since we both created another table, and two table triggers. That means that we need to drop those as well when we do a rollback.

1public function down(): void
2{
3 DB::statement('DROP TRIGGER IF EXISTS insert_order_trigger;');
4 DB::statement('DROP TRIGGER IF EXISTS update_order_trigger;');
5 Schema::dropIfExists('orders');
6 Schema::dropIfExists('order_logs');
7}

After we have migrated the database we can take a look at it using SHOW TABLES command.

1MariaDB [log_demo]> show tables;
2+------------------------+
3| Tables_in_log_demo |
4+------------------------+
5| failed_jobs |
6| migrations |
7| order_logs |
8| orders |
9| password_resets |
10| personal_access_tokens |
11| users |
12+------------------------+
137 rows in set (0.014 sec)

We have our two tables, orders and order_logs. We can also check that our triggers are created properly by using the SHOW TRIGGERS command.

1MariaDB [log_demo]> show triggers like 'orders%'\G;
2*************************** 1. row ***************************
3 Trigger: insert_order_trigger
4 Event: INSERT
5 Table: orders
6 Statement: BEGIN
7 INSERT INTO order_logs(action, order_id, old_status, new_status, created_at)
8 VALUES('INSERT', NEW.id, NULL, NEW.status, NOW());
9 END
10 Timing: AFTER
11 Created: 2022-10-05 05:33:34.01
12 sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
13 Definer: root@localhost
14character_set_client: utf8mb4
15collation_connection: utf8mb4_unicode_ci
16 Database Collation: utf8mb4_general_ci
17*************************** 2. row ***************************
18 Trigger: update_order_trigger
19 Event: UPDATE
20 Table: orders
21 Statement: BEGIN
22 INSERT INTO order_logs(action, order_id, old_status, new_status, created_at)
23 VALUES('UPDATE', NEW.id, OLD.status, NEW.status, NOW());
24 END
25 Timing: AFTER
26 Created: 2022-10-05 05:33:34.14
27 sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
28 Definer: root@localhost
29character_set_client: utf8mb4
30collation_connection: utf8mb4_unicode_ci
31 Database Collation: utf8mb4_general_ci
322 rows in set (0.009 sec)

A lot of information there, but the only parts we need to care about for now, is that we have two rows, and that they have the events, UPDATE and INSERT.

Now we can try it out by creating an order. I assume that you know how to create an Order model and a factory for it, so I will not go into that.

1Order::factory()->create([
2 'status' => 1,
3 'user_id' => 1,
4]);

So if we have done everything correctly, we should have an order in our orders table, and we should have a record in our order_logs table with the INSERT action. Let's take a look.

1MariaDB [log_demo]> select * from orders;
2+----+--------+---------+---------------------+---------------------+
3| id | status | user_id | created_at | updated_at |
4+----+--------+---------+---------------------+---------------------+
5| 1 | 1 | 1 | 2022-10-05 03:54:44 | 2022-10-05 03:54:44 |
6+----+--------+---------+---------------------+---------------------+
71 row in set (0.000 sec)

We do have one order with status 1 as expected, how about the log entry?

1MariaDB [log_demo]> select * from order_logs;
2+----+----------+--------+------------+------------+---------------------+---------------------+
3| id | order_id | action | old_status | new_status | created_at | updated_at |
4+----+----------+--------+------------+------------+---------------------+---------------------+
5| 1 | 1 | INSERT | NULL | 1 | 2022-10-05 05:54:44 | 2022-10-05 05:54:44 |
6+----+----------+--------+------------+------------+---------------------+---------------------+
71 row in set (0.000 sec)

Now isn't that really cool, or is it just me who is amazed by what the database can do for us?

So what about updates, we will test that now. We will make updates to the order for each of the statuses that we talked about earlier.

1$order = Order::findOrFail(1);
2$order->status = 2;
3$order->save();

Now that we have updated the status let's take a look in our two tables.

1MariaDB [log_demo]> select * from orders;
2+----+--------+---------+---------------------+---------------------+
3| id | status | user_id | created_at | updated_at |
4+----+--------+---------+---------------------+---------------------+
5| 1 | 2 | 1 | 2022-10-05 03:54:44 | 2022-10-05 16:34:30 |
6+----+--------+---------+---------------------+---------------------+
71 row in set (0.000 sec)
8 
9MariaDB [log_demo]> select * from order_logs;
10+----+----------+--------+------------+------------+---------------------+---------------------+
11| id | order_id | action | old_status | new_status | created_at | updated_at |
12+----+----------+--------+------------+------------+---------------------+---------------------+
13| 1 | 1 | INSERT | NULL | 1 | 2022-10-05 05:54:44 | 2022-10-05 05:54:44 |
14| 2 | 1 | UPDATE | 1 | 2 | 2022-10-05 18:34:30 | 2022-10-05 18:34:30 |
15+----+----------+--------+------------+------------+---------------------+---------------------+
162 rows in set (0.000 sec)

Pretty sweet right? I won't make you read through me updating every status one by one, but when the order has reached the status delivered the order_logs should look something like this.

1MariaDB [log_demo]> select * from order_logs;
2+----+----------+--------+------------+------------+---------------------+---------------------+
3| id | order_id | action | old_status | new_status | created_at | updated_at |
4+----+----------+--------+------------+------------+---------------------+---------------------+
5| 1 | 1 | INSERT | NULL | 1 | 2022-10-05 05:54:44 | 2022-10-05 05:54:44 |
6| 2 | 1 | UPDATE | 1 | 2 | 2022-10-05 18:34:30 | 2022-10-05 18:34:30 |
7| 3 | 1 | UPDATE | 2 | 3 | 2022-10-05 18:39:59 | 2022-10-05 18:39:59 |
8| 4 | 1 | UPDATE | 3 | 4 | 2022-10-05 18:40:03 | 2022-10-05 18:40:03 |
9| 5 | 1 | UPDATE | 4 | 5 | 2022-10-05 18:40:08 | 2022-10-05 18:40:08 |
10| 6 | 1 | UPDATE | 5 | 6 | 2022-10-05 18:40:12 | 2022-10-05 18:40:12 |
11+----+----------+--------+------------+------------+---------------------+---------------------+
126 rows in set (0.000 sec)

We have now accomplished the task we were given, however they aren't fully satisfied with the result, they also realized that they need to know who has done each step. This presents a bit of a challenge, not because the code is really complex to get the database user, but rather that all connections done to the database by our application uses the same user.

Let's start by adding a changed_by column to our order_logs table, and update the triggers accordingly. We use the USER() function to get the user from MySQL.

1Schema::create('order_logs', function (Blueprint $table) {
2 $table->id();
3 $table->unsignedBigInteger('order_id');
4 $table->string('action');
5 $table->integer('old_status')->nullable();
6 $table->integer('new_status');
7 $table->string('changed_by');
8 $table->timestamps();
9 });
10 
11 DB::statement("CREATE TRIGGER insert_order_trigger AFTER INSERT
12 ON orders FOR EACH ROW
13 BEGIN
14 INSERT INTO order_logs(action, order_id, old_status, new_status, changed_by, created_at, updated_at)
15 VALUES('INSERT', NEW.id, NULL, NEW.status, USER(), NOW(), NOW());
16 END;
17 ");
18 DB::statement("CREATE TRIGGER update_order_trigger AFTER UPDATE
19 ON orders FOR EACH ROW
20 BEGIN
21 INSERT INTO order_logs(action, order_id, old_status, new_status, changed_by, created_at, updated_at)
22 VALUES('UPDATE', NEW.id, OLD.status, NEW.status, USER(), NOW(), NOW());
23 END;
24 ");

Now let's insert an order into the orders table just like we did before, and do a select on the order_logs.

1MariaDB [log_demo]> select * from order_logs;
2+----+----------+--------+------------+------------+----------------+---------------------+---------------------+
3| id | order_id | action | old_status | new_status | changed_by | created_at | updated_at |
4+----+----------+--------+------------+------------+----------------+---------------------+---------------------+
5| 1 | 1 | INSERT | NULL | 1 | root@localhost | 2022-10-05 18:55:11 | 2022-10-05 18:55:11 |
6+----+----------+--------+------------+------------+----------------+---------------------+---------------------+
71 row in set (0.000 sec)

Hey wait a minute, we sure as hell doesn't want to have the database user here, we want the application user, Well we can try changing the USER() to CURRENT_USER() since according to the internet, they can give different results, so let's give it a try.

1MariaDB [log_demo]> select * from order_logs;
2+----+----------+--------+------------+------------+----------------+---------------------+---------------------+
3| id | order_id | action | old_status | new_status | changed_by | created_at | updated_at |
4+----+----------+--------+------------+------------+----------------+---------------------+---------------------+
5| 1 | 1 | INSERT | NULL | 1 | root@localhost | 2022-10-05 19:02:17 | 2022-10-05 19:02:17 |
6+----+----------+--------+------------+------------+----------------+---------------------+---------------------+
71 row in set (0.000 sec)

No such luck, this is due to the fact that there is a big difference between a database user and an application user. There area few ways to get around this, and we will look at two options, one bad and one pretty good. Let's start with the bad one.

We could create a database user for each user on our system and change the user who connects to the database, depending on who is logged in. While this would work great inside the database, it would generate unneeded complexity to our application on the php side.

The better solution would be to store the user who update the order in the orders table. Let's give that a go shall we?

We start with the migrations and add the changed_by column to the orders table as well.

1Schema::create('orders', function (Blueprint $table) {
2 $table->id();
3 $table->integer('status');
4 $table->foreignIdFor(User::class);
5 $table->string('changed_by');
6 $table->timestamps();
7 });

We also need to update our triggers.

1DB::statement("CREATE TRIGGER insert_order_trigger AFTER INSERT
2 ON orders FOR EACH ROW
3 BEGIN
4 INSERT INTO order_logs(action, order_id, old_status, new_status, changed_by, created_at, updated_at)
5 VALUES('INSERT', NEW.id, NULL, NEW.status, NEW.changed_by, NOW(), NOW());
6 END;
7 ");
8DB::statement("CREATE TRIGGER update_order_trigger AFTER UPDATE
9 ON orders FOR EACH ROW
10 BEGIN
11 INSERT INTO order_logs(action, order_id, old_status, new_status, changed_by, created_at, updated_at)
12 VALUES('UPDATE', NEW.id, OLD.status, NEW.status, NEW.changed_by, NOW(), NOW());
13 END;
14 ");

Now after we migrated again we need to change the way we create the record so that we get the authenticated user. We create a user so that we can pass that user's name to the orders table.

1$user = User::factory()->create();
2Order::factory()->create([
3 'status' => 1,
4 'user_id' => 1,
5 'changed_by' => $user->name,
6]);

Now if we look at the orders and order_logs tables we see this.

1MariaDB [log_demo]> select * from orders;
2+----+--------+---------+-----------------+---------------------+---------------------+
3| id | status | user_id | changed_by | created_at | updated_at |
4+----+--------+---------+-----------------+---------------------+---------------------+
5| 1 | 1 | 1 | Susana Donnelly | 2022-10-05 17:31:22 | 2022-10-05 17:31:22 |
6+----+--------+---------+-----------------+---------------------+---------------------+
71 row in set (0.000 sec)
8 
9MariaDB [log_demo]> select * from order_logs;
10+----+----------+--------+------------+------------+-----------------+---------------------+---------------------+
11| id | order_id | action | old_status | new_status | changed_by | created_at | updated_at |
12+----+----------+--------+------------+------------+-----------------+---------------------+---------------------+
13| 1 | 1 | INSERT | NULL | 1 | Susana Donnelly | 2022-10-05 19:31:22 | 2022-10-05 19:31:22 |
14+----+----------+--------+------------+------------+-----------------+---------------------+---------------------+
151 row in set (0.000 sec)

This is what we wanted, however the changed_by should be the username and not the user's name, but that is something I will not cover in this post since it has nothing to do with the triggers at all. The reason for putting the name in plain text instead of using a foreign key, is that I don't want to have any relationships with the order_logs table, this is my preference, and you may do as you see fit.

Now let's update the order. I created a new user for each step just to prove the concept, and I made all the steps behind the scenes.

1$user = User::factory()->create();
2 
3$order = Order::findOrFail(1);
4$order->status = 6;
5$order->changed_by = $user->name;
6$order->save();

You should of course use Auth::user()->name when creating and updating the orders.

1MariaDB [log_demo]> select * from order_logs;
2+----+----------+--------+------------+------------+--------------------+---------------------+---------------------+
3| id | order_id | action | old_status | new_status | changed_by | created_at | updated_at |
4+----+----------+--------+------------+------------+--------------------+---------------------+---------------------+
5| 1 | 1 | INSERT | NULL | 1 | Susana Donnelly | 2022-10-05 19:31:22 | 2022-10-05 19:31:22 |
6| 2 | 1 | UPDATE | 1 | 2 | Demarcus Mueller | 2022-10-05 19:45:54 | 2022-10-05 19:45:54 |
7| 3 | 1 | UPDATE | 2 | 3 | Lavina Stroman | 2022-10-05 19:45:58 | 2022-10-05 19:45:58 |
8| 4 | 1 | UPDATE | 3 | 4 | Rosalyn Mertz Jr. | 2022-10-05 19:46:02 | 2022-10-05 19:46:02 |
9| 5 | 1 | UPDATE | 4 | 5 | Carmel Hand | 2022-10-05 19:46:08 | 2022-10-05 19:46:08 |
10| 6 | 1 | UPDATE | 5 | 6 | Catalina VonRueden | 2022-10-05 19:46:13 | 2022-10-05 19:46:13 |
11+----+----------+--------+------------+------------+--------------------+---------------------+---------------------+
126 rows in set (0.000 sec)

Now ain't that grand, we can see who made each step, and even if we need to revert to a previous status, it will be logged, and we will know who did it and when it was done.

If there are more than one table that you want to log all the actions in, it might not be the best of ideas to create a log table for each table, but rather create one log table that is generic enough to handle any table you throw at it. Let's give that a try.

We start with creating the logs table.

1Schema::create('logs', function (Blueprint $table) {
2 $table->id();
3 $table->string('table_name');
4 $table->string('column_name');
5 $table->unsignedBigInteger('primary_key');
6 $table->integer('old_value')->nullable();
7 $table->integer('new_value')->nullable();
8 $table->string('action');
9 $table->string('changed_by');
10 $table->timestamps();
11 });

As you can see there are some differences between the order_logs table and the new one. Since the entry can come from any table we need to know which table the record belongs to, and we also need to know which column was changed, and last but not least we need to know which primary key it refers to.

Next up is to update our triggers to use the new logs table. We will still be using the orders table in our example.

1DB::statement("CREATE TRIGGER insert_order_trigger AFTER INSERT
2 ON orders FOR EACH ROW
3 BEGIN
4 INSERT INTO logs(table_name, column_name, primary_key, old_value, new_value, action, changed_by, created_at, updated_at)
5 VALUES('ORDERS', 'id', NEW.id, NULL, NEW.id, 'INSERT', NEW.changed_by, NOW(), NOW());
6 INSERT INTO logs(table_name, column_name, primary_key, old_value, new_value, action, changed_by, created_at, updated_at)
7 VALUES('ORDERS', 'status', NEW.id, NULL, NEW.status, 'INSERT', NEW.changed_by, NOW(), NOW());
8 END;
9 ");
10DB::statement("CREATE TRIGGER update_order_trigger AFTER UPDATE
11 ON orders FOR EACH ROW
12 BEGIN
13 INSERT INTO logs(table_name, column_name, primary_key, old_value, new_value, action, changed_by, created_at, updated_at)
14 VALUES('ORDERS', 'id', OLD.id, OLD.id, OLD.id, 'UPDATE', NEW.changed_by, NOW(), NOW());
15 INSERT INTO logs(table_name, column_name, primary_key, old_value, new_value, action, changed_by, created_at, updated_at)
16 VALUES('ORDERS', 'status', OLD.id, OLD.status, NEW.status, 'UPDATE', NEW.changed_by, NOW(), NOW());
17 END;
18 ");

I know it's a bit silly to log the id column since it will never ever change, but please just roll with the example. It would have been even worse to log the changed_by column. You will need to create an insert statement for each column that you want to track the changes for.

Now lets run create one order and see if our insert trigger works.

1$user = User::factory()->create();
2Order::factory()->create([
3 'status' => 1,
4 'user_id' => 1,
5 'changed_by' => $user->name,
6]);

This is the result of that creation.

1MariaDB [log_demo]> select * from orders;
2+----+--------+---------+---------------+---------------------+---------------------+
3| id | status | user_id | changed_by | created_at | updated_at |
4+----+--------+---------+---------------+---------------------+---------------------+
5| 1 | 1 | 1 | Parker O\'Hara | 2022-10-21 17:47:26 | 2022-10-21 17:47:26 |
6+----+--------+---------+---------------+---------------------+---------------------+
71 row in set (0,000 sec)
8 
9MariaDB [log_demo]> select * from logs;
10+----+------------+-------------+-------------+-----------+-----------+--------+---------------+---------------------+---------------------+
11| id | table_name | column_name | primary_key | old_value | new_value | action | changed_by | created_at | updated_at |
12+----+------------+-------------+-------------+-----------+-----------+--------+---------------+---------------------+---------------------+
13| 1 | ORDERS | id | 1 | NULL | 1 | INSERT | Parker O\'Hara | 2022-10-21 19:47:26 | 2022-10-21 19:47:26 |
14| 2 | ORDERS | status | 1 | NULL | 1 | INSERT | Parker O\'Hara | 2022-10-21 19:47:26 | 2022-10-21 19:47:26 |
15+----+------------+-------------+-------------+-----------+-----------+--------+---------------+---------------------+---------------------+
162 rows in set (0,000 sec)

So what then happens if we run the status updates two through to six?

1MariaDB [log_demo]> select * from logs;
2+----+------------+-------------+-------------+-----------+-----------+--------+---------------------+---------------------+---------------------+
3| id | table_name | column_name | primary_key | old_value | new_value | action | changed_by | created_at | updated_at |
4+----+------------+-------------+-------------+-----------+-----------+--------+---------------------+---------------------+---------------------+
5| 1 | ORDERS | id | 1 | NULL | 1 | INSERT | Parker O\'Hara | 2022-10-21 19:55:44 | 2022-10-21 19:55:44 |
6| 2 | ORDERS | status | 1 | NULL | 1 | INSERT | Parker O\'Hara | 2022-10-21 19:55:44 | 2022-10-21 19:55:44 |
7| 3 | ORDERS | id | 1 | 1 | 1 | UPDATE | Gus Spinka | 2022-10-21 19:55:58 | 2022-10-21 19:55:58 |
8| 4 | ORDERS | status | 1 | 1 | 2 | UPDATE | Gus Spinka | 2022-10-21 19:55:58 | 2022-10-21 19:55:58 |
9| 5 | ORDERS | id | 1 | 1 | 1 | UPDATE | Sadye Predovic | 2022-10-21 19:56:02 | 2022-10-21 19:56:02 |
10| 6 | ORDERS | status | 1 | 2 | 3 | UPDATE | Sadye Predovic | 2022-10-21 19:56:02 | 2022-10-21 19:56:02 |
11| 7 | ORDERS | id | 1 | 1 | 1 | UPDATE | Dimitri Kohler | 2022-10-21 19:56:05 | 2022-10-21 19:56:05 |
12| 8 | ORDERS | status | 1 | 3 | 4 | UPDATE | Dimitri Kohler | 2022-10-21 19:56:05 | 2022-10-21 19:56:05 |
13| 9 | ORDERS | id | 1 | 1 | 1 | UPDATE | Courtney Mraz | 2022-10-21 19:56:10 | 2022-10-21 19:56:10 |
14| 10 | ORDERS | status | 1 | 4 | 5 | UPDATE | Courtney Mraz | 2022-10-21 19:56:10 | 2022-10-21 19:56:10 |
15| 11 | ORDERS | id | 1 | 1 | 1 | UPDATE | Kathryne Gleason MD | 2022-10-21 19:56:16 | 2022-10-21 19:56:16 |
16| 12 | ORDERS | status | 1 | 5 | 6 | UPDATE | Kathryne Gleason MD | 2022-10-21 19:56:16 | 2022-10-21 19:56:16 |
17+----+------------+-------------+-------------+-----------+-----------+--------+---------------------+---------------------+---------------------+
1812 rows in set (0,000 sec)

You can do a lot more with table triggers if you like, you just have to learn a little about the programming language of the database, I'm not sure what it is actually called for MySQL/MariaDB, but for Oracle databases it's called PL/SQL and for Microsoft SQLServer it's called T-SQL. I suggest doing a Google search on "MySQL stored procedures", and of course do some more reading about tabletriggers, since we only scratched the surface on what you can do with them.

I hope you enjoyed this post, and as usual a big thank you to @rsinnbeck for the help on editing.

//Tray2

© 2024 Tray2.se. All rights reserved.

This site uses Torchlight for syntax highlighting.