Question

Boiled down to the essence of my problem I have a MySQL table (InnoDB) with a DATETIME field and I need to implement a duplicate check that does not allow the same date, down to the hour, to be used more than once (eg. only one row with 2013-07-18 13:xx:xx).

My first question then is, if there any way to enforce this in the MySQL database itself?

Otherwise my approach would be along the lines of:

  1. Lock the table for both read and writes (to avoid the
  2. Make a SELECT query to verify I can insert the new row
  3. Insert it
  4. Unlock the table again

I really dislike this solution - any suggestions on how to do this without having to lock the table would be appreciated.

Was it helpful?

Solution

There's no simple, declarative way to do this in MySQL. But you can create a shadowing column, and use triggers to keep the data consistent. This assumes that "ts" (below) can be any valid timestamp, but that you want only one of them per hour.

create table test (
   ts datetime not null,
   ts_uniq char(13) not null,
   unique (ts_uniq)
);

The column "ts_uniq" is the shadowing column. It will hold strings like '2013-01-01 08'.

create trigger bef_ins_test 
before insert on test
for each row
set new.ts_uniq = date_format(new.ts, '%Y-%m-%d %H');

You'll need a similar trigger that executes before updates.

create trigger bef_upd_test 
before update on test
for each row
set new.ts_uniq = date_format(new.ts, '%Y-%m-%d %H');

When you insert values for "ts", the shadowing column is automatically set correctly.

insert into test (ts) values ('2013-01-01 08:35:33');
select * from test;

ts                   ts_uniq
--
2013-01-01 08:35:33  2013-01-01 08

Trying to insert a slightly different value fails correctly, raising error code 1062 (duplicate entry).

insert into test (ts) values ('2013-01-01 08:47:13');

If you update the existing timestamp, the BEFORE UPDATE trigger keeps the column "ts_uniq" consistent.

update test
set ts = '2013-01-01 17:42:42';

select * from test;

ts                   ts_uniq
--
2013-01-01 17:42:42  2013-01-01 17

Trying to independently update "ts_uniq" won't raise an error, but it won't change the row, either.

update test
set ts_uniq = '2013-12-31 18';

select * from test;

ts                   ts_uniq
--
2013-01-01 17:42:42  2013-01-01 17
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top