Meena Alfons
Meena Alfons

Reputation: 1230

MySQL: How to implement consistency constraints?

Example:

Here is the employee table:

CREATE TABLE `employees` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `code` varchar(4) NOT NULL,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
);

The code is a simple login code of 4 characters. Soft delete is implemented using deleted_at field. Current employees are those with deleted_at=NULL.

We need to keep the code unique between the current employees.

Using a UNIQUE Constraints on the code field will prevent current employees from using codes that have been used by a soft-deleted employee.

How to enforce this constraint?

This is an example of the general problem of how to enforce consistency constraints in MySQL.

Edit:

The schema could be changed to make use of unique constraints as @bill-karwin suggests.

What about applying complex consistency constraints that may span multiple tables?

Upvotes: 0

Views: 793

Answers (2)

Bill Karwin
Bill Karwin

Reputation: 562328

One solution is to create a nullable column is_active that is restricted to either NULL or a single non-NULL value. The columns code and is_active together must be unique.

CREATE TABLE `employees` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `code` varchar(4) NOT NULL,
  `is_active` enum('yes'),
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY (`code`, `is_active`)
);

If is_active is NULL, then it allows any number of duplicates in the code column. If is_active is not NULL, then it allows only one value 'yes' and therefore each value in the code column must be unique.

deleted_at no longer indicates if the employee is currently active or not, only when then were inactivated.


Re your comment:

Constraints that span multiple tables are called ASSERTIONS in the SQL standard, but there is literally no RDBMS product that implements that feature from the standard.

Some implement constraints with triggers, but it's not always obvious how to design triggers to do what you want efficiently.

Honestly, most people resort to application logic for these sorts of constraints. This comes with some risk of race conditions. As soon as you do a SELECT statement to verify the data satisfies the constraints, some other concurrent session may commit data that spoils the constraint before your session can commit its changes.

The only solution is to use pessimistic locking to ensure no other session can jump ahead of you.

Upvotes: 0

Nick
Nick

Reputation: 147166

One relatively simple solution to your problem would be to change the deleted_at column to default to something other than NULL (e.g. '1900-01-01', or even the "zero" date '0000-00-00' if you have them enabled). You can then create a UNIQUE index on (code, deleted_at) which would prevent any employee from using a code which a current employee had (since you would get a match on (code,default)), but not exclude them using a code which a previous employee had used, since the default value would not match the deleted_at timestamp.

Upvotes: 1

Related Questions