I like this one...but I tested it a bit and the bad part is that doing a select 
on t requires a complete table scan.  So finding these values is too slow.
I did have to modify a bit to get it to work correctly...now I'll try one of 
the other suggestions.

sqlite> create table t (nm integer primary key);
sqlite> create view tv as select (nm & 1073741823) as n, nm >> 30 as m from t;
sqlite> create trigger tv_ins instead of insert on tv begin
   ...> select case when typeof(NEW.n) != 'integer' then raise(fail,'n is not 
an integer') end;
   ...> select case when typeof(NEW.m) != 'integer' then raise(fail,'m is not 
an integer') end;
   ...> select case when NEW.m > (1 << 30) then raise(fail, 'n is too large') 
end;
   ...> select case when NEW.n < 0 then raise(fail, 'n must be non-negative') 
end;
   ...> select case when NEW.m < 0 then raise(fail, 'm must be non-negative') 
end;
   ...> select case (select count(*) from t where (nm = (NEW.m << 30) | NEW.n) 
or (nm = (New.n << 3
) | New.m)) when 1 then raise(fail, 'This relation already exists!') end;
   ...> insert into t select New.n | (New.m << 30);
   ...> end;
sqlite> insert into tv (n,m) values(1,2);
sqlite> insert into tv (n,m) values(1,2);
Error: This relation already exists!
sqlite> insert into tv(n,m) values(3,1);
sqlite> insert into tv(n,m) values(3,2);
sqlite> insert into tv(n,m) values(5,4);
sqlite> insert into tv(n,m) values(4,5);
Error: This relation already exists!
sqlite> select * from tv;
3|1
1|2
3|2
5|4

Michael D. Black
Senior Scientist
NG Information Systems
Advanced Analytics Directorate



________________________________________
From: sqlite-users-boun...@sqlite.org [sqlite-users-boun...@sqlite.org] on 
behalf of Nico Williams [n...@cryptonector.com]
Sent: Wednesday, February 09, 2011 2:28 PM
To: General Discussion of SQLite Database
Subject: EXT :Re: [sqlite] Bi-directional unique

On Wed, Feb 9, 2011 at 2:00 PM, Nico Williams <n...@cryptonector.com> wrote:
> This way an insert into t requires one lookup and update in t_idx and
> one lookup and update in the table's implied rowid index.  I.e., twice
> the work of an insert without this additional index and trigger.

Also, you get double the DB size this way.

Using Jim's suggestion you get to avoid those problems:

sqlite> create table t (nm integer primary key);
sqlite> create view tv as select nm >> 30 as n, (nm & (1 << 30)) >> 30
as m from t;
sqlite> create trigger tv_ins instead of insert on tv begin
   ...> select case when typeof(NEW.n) != 'integer' then raise(fail,
'n is not an integer') end;
   ...> select case when typeof(NEW.m) != 'integer' then raise(fail,
'm is not an integer') end;
   ...> select case when NEW.n > (1 << 30) then raise(fail, 'n is too
large') end;
   ...> select case when NEW.m > (1 << 30) then raise(fail, 'm is too
large') end;
   ...> select case when NEW.n < 0 then raise(fail, 'n must be
non-negative') end;
   ...> select case when NEW.m < 0 then raise(fail, 'm must be
non-negative') end;
   ...> select case (select count(*) from t where nm = NEW.n << 30 |
NEW.m) when 1 then raise(fail, 'This relation already exists!') end;
   ...> insert into t select NEW.m << 30 | NEW.n;
   ...> end;
sqlite> insert into tv (n, m) values (1, 2);
sqlite> insert into tv (n, m) values (1, 2);
Error: PRIMARY KEY must be unique
sqlite> insert into tv (n, m) values (3, 1);
sqlite> insert into tv (n, m) values (3, 2);
sqlite> insert into tv (n, m) values (1, 3);
Error: This relation already exists!
sqlite>

This still requires two rowid index lookups for every insert, but only
one update.

Nico
--
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to