1

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.

10
  • Store the date and time components separately to different columns and create unique constraint based on that? You just require DATE part paired with hour, I'd save the entire datetime to 1 column, date part to another and hour component to third column. Unique key on (date, hour) will give you what you need. Commented Jul 18, 2013 at 12:02
  • @N.B.: You'd also need a trigger to guarantee the datetime column always matched other two columns. Commented Jul 18, 2013 at 12:06
  • @MikeSherrill'Catcall' that makes no sense, sorry. If you care to elaborate, please do so. Commented Jul 18, 2013 at 12:07
  • @N.B.: Imagine inserting a tuple like {2013-01-01 08:00, 2013-01-01, 8}. Updating the first column to '2013-03-31 17:00' corrupts the data, but doesn't affect the unique constraint. You need a trigger to make sure that updates like that leave the data in a consistent state. Commented Jul 18, 2013 at 12:13
  • 1
    How much easier would that be with a modern DBMS that supports function based indexes.... Commented Jul 18, 2013 at 12:17

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.