The extra space would just be the few bytes to store the text, which is nothing 
compared to the "extra" index size. And since it's "infrequent update" then it 
wouldn't be too bad for the extra time. The normal way would have an extra 
index to update, the trigger way would be less efficient. On an in-memory test 
of 10,000,000 updates, the trigger way took 14% more time, and only 2% more 
when I did it on disk. The "extra" unique index added 36% to the page count.

I will say that the "debugging nightmare" comment is 100% absolutely true 
though, and is why I'd personally bite the bullet and go with the unique 
keyword. I'm just pointing out how it would be done.



--just testing output below this line--



SQLite version 3.17.0 2017-02-13 16:02:40
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.

sqlite> create table foo
   ...> (
   ...>   Id integer primary key,
   ...>   UniqueCol int unique,
   ...>   ExtraCol int
   ...> );
Run Time: real 0.000 user 0.000000 sys 0.000000

sqlite> create index fooIndex on foo (UniqueCol, ExtraCol);
Run Time: real 0.000 user 0.000000 sys 0.000000

sqlite> explain query plan insert into foo values (1, 1, 1);
Run Time: real 0.000 user 0.000000 sys 0.000000

sqlite> explain insert into foo values (1, 1, 1);
addr  opcode         p1    p2    p3    p4             p5  comment
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     28    0                    00  Start at 28
1     OpenWrite      0     2     0     3              00  root=2 iDb=0; foo
2     OpenWrite      1     4     0     k(3,,,)        00  root=4 iDb=0; fooIndex
3     OpenWrite      2     3     0     k(2,,)         00  root=3 iDb=0; 
sqlite_autoindex_foo_1
4     Integer        1     1     0                    00  r[1]=1
5     NotNull        1     7     0                    00  if r[1]!=NULL goto 7
6     NewRowid       0     1     0                    00  r[1]=rowid
7     MustBeInt      1     0     0                    00
8     SoftNull       2     0     0                    00  r[2]=NULL
9     Integer        1     3     0                    00  r[3]=1
10    Integer        1     4     0                    00  r[4]=1
11    NotExists      0     13    1                    00  intkey=r[1]
12    Halt           1555  2     0     foo.Id         02
13    Affinity       2     3     0     DDD            00  affinity(r[2..4])
14    SCopy          3     6     0                    00  r[6]=r[3]; UniqueCol
15    SCopy          4     7     0                    00  r[7]=r[4]; ExtraCol
16    IntCopy        1     8     0                    00  r[8]=r[1]; rowid
17    MakeRecord     6     3     5                    00  r[5]=mkrec(r[6..8]); 
for fooIndex
18    SCopy          3     10    0                    00  r[10]=r[3]; UniqueCol
19    IntCopy        1     11    0                    00  r[11]=r[1]; rowid
20    MakeRecord     10    2     9                    00  
r[9]=mkrec(r[10..11]); for sqlite_autoindex_foo_1
21    NoConflict     2     23    10    1              00  key=r[10]
22    Halt           2067  2     0     foo.UniqueCol  02
23    IdxInsert      1     5     6     3              10  key=r[5]
24    IdxInsert      2     9     10    2              10  key=r[9]
25    MakeRecord     2     3     12                   00  r[12]=mkrec(r[2..4])
26    Insert         0     12    1     foo            31  intkey=r[1] data=r[12]
27    Halt           0     0     0                    00
28    Transaction    0     1     2     0              01  usesStmtJournal=0
29    Goto           0     1     0                    00
Run Time: real 0.031 user 0.000000 sys 0.000000



sqlite> create table bar
   ...> (
   ...>   Id integer primary key,
   ...>   UniqueCol int,
   ...>   ExtraCol int
   ...> );
Run Time: real 0.000 user 0.000000 sys 0.000000

sqlite> create index barIndex on bar (UniqueCol, ExtraCol);
Run Time: real 0.000 user 0.000000 sys 0.000000

sqlite> create trigger bar_trg_UniqueCol_insert
   ...> before insert on bar
   ...> for each row
   ...> when exists (select 1 from bar where UniqueCol = new.UniqueCol)
   ...> begin select raise(abort, 'UNIQUE constraint failed: bar.UniqueCol'); 
end;
Run Time: real 0.000 user 0.000000 sys 0.000000

sqlite> create trigger bar_trg_UniqueCol_update
   ...> before update of UniqueCol on bar
   ...> for each row
   ...> when exists (select 1 from bar where UniqueCol = new.UniqueCol)
   ...> begin select raise(abort, 'UNIQUE constraint failed: bar.UniqueCol'); 
end;
Run Time: real 0.000 user 0.000000 sys 0.000000

sqlite> explain insert into bar values (1, 1, 1);
addr  opcode         p1    p2    p3    p4             p5  comment
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     33    0                    00  Start at 33
1     OpenWrite      0     5     0     3              00  root=5 iDb=0; bar
2     OpenWrite      1     6     0     k(3,,,)        00  root=6 iDb=0; barIndex
3     Integer        1     9     0                    00  r[9]=1
4     NotNull        9     6     0                    00  if r[9]!=NULL goto 6
5     Integer        -1    9     0                    00  r[9]=-1
6     MustBeInt      9     0     0                    00
7     Integer        1     10    0                    00  r[10]=1
8     Copy           10    13    0                    00  r[13]=r[10]
9     Integer        1     11    0                    00  r[11]=1
10    Copy           11    14    0                    00  r[14]=r[11]
11    Integer        1     12    0                    00  r[12]=1
12    Copy           12    15    0                    00  r[15]=r[12]
13    Affinity       10    3     0     DDD            00  affinity(r[10..12])
14    Program        5     32    16    program        01  Call: 
bar_trg_UniqueCol_insert.default
15    Copy           13    1     0                    00  r[1]=r[13]
16    NotNull        1     18    0                    00  if r[1]!=NULL goto 18
17    NewRowid       0     1     0                    00  r[1]=rowid
18    MustBeInt      1     0     0                    00
19    SoftNull       2     0     0                    00  r[2]=NULL
20    Copy           14    3     0                    00  r[3]=r[14]
21    Copy           15    4     0                    00  r[4]=r[15]
22    NotExists      0     24    1                    00  intkey=r[1]
23    Halt           1555  2     0     bar.Id         02
24    Affinity       2     3     0     DDD            00  affinity(r[2..4])
25    SCopy          3     6     0                    00  r[6]=r[3]; UniqueCol
26    SCopy          4     7     0                    00  r[7]=r[4]; ExtraCol
27    IntCopy        1     8     0                    00  r[8]=r[1]; rowid
28    MakeRecord     6     3     5                    00  r[5]=mkrec(r[6..8]); 
for barIndex
29    IdxInsert      1     5     6     3              10  key=r[5]
30    MakeRecord     2     3     17                   00  r[17]=mkrec(r[2..4])
31    Insert         0     17    1     bar            31  intkey=r[1] data=r[17]
32    Halt           0     0     0                    00
33    Transaction    0     1     6     0              01  usesStmtJournal=1
34    Goto           0     1     0                    00
0     Init           0     1     0     -- TRIGGER bar_trg_UniqueCol_insert  00  
Start at 1; Start: bar_trg_UniqueCol_insert.default (BEFORE INSERT ON bar)
1     Once           0     13    0                    00
2     Integer        0     2     0                    00  r[2]=0; Init EXISTS 
result
3     Integer        1     3     0                    00  r[3]=1; LIMIT counter
4     OpenRead       1     6     0     k(3,,,)        02  root=6 iDb=0; barIndex
5     Param          6     4     0                    00  new.UniqueCol -> $4
6     IsNull         4     13    0                    00  if r[4]==NULL goto 13
7     Affinity       4     1     0     D              00  affinity(r[4])
8     SeekGE         1     13    4     1              00  key=r[4]
9       IdxGT          1     13    4     1              00  key=r[4]
10      Integer        1     2     0                    00  r[2]=1
11      DecrJumpZero   3     13    0                    00  if (--r[3])==0 goto 
13
12    Next           1     9     0                    00
13    IfNot          2     15    1                    00
14    Halt           1811  2     0     UNIQUE constraint failed: bar.UniqueCol  
00
15    Halt           0     0     0                    00  End: 
bar_trg_UniqueCol_insert.default
Run Time: real 0.063 user 0.000000 sys 0.015600


--saved to disk and vacuumed here

sqlite> with recursive x (Id, UniqueCol, ExtraCol) as (values (1, 1, 1) union 
all select Id + 1, UniqueCol + 1, ExtraCol + 1 from x limit 10000000) insert 
into foo select * from x;
--EQP-- 3,0,0,SCAN TABLE x
--EQP-- 1,0,0,COMPOUND SUBQUERIES 0 AND 0 (UNION ALL)
--EQP-- 0,0,0,SCAN SUBQUERY 1
Run Time: real 26.020 user 21.762140 sys 1.232408

sqlite> with recursive x (Id, UniqueCol, ExtraCol) as (values (1, 1, 1) union 
all select Id + 1, UniqueCol + 1, ExtraCol + 1 from x limit 10000000) insert 
into bar select * from x;
--EQP-- 3,0,0,SCAN TABLE x
--EQP-- 1,0,0,COMPOUND SUBQUERIES 0 AND 0 (UNION ALL)
--EQP-- 0,0,0,SCAN SUBQUERY 1
Run Time: real 26.535 user 23.914953 sys 1.388409

sqlite> analyze;
Run Time: real 3.557 user 2.652017 sys 0.904806

sqlite> vacuum;
Run Time: real 41.371 user 12.043277 sys 8.408454

--ran sqlite3_analyzer
*** Page counts for all tables with their indices *****************************

FOO............................................... 112646      57.7% 
BAR............................................... 82440       42.3% 
SQLITE_MASTER..................................... 1            0.0% 
SQLITE_STAT1...................................... 1            0.0% 

*** Page counts for all tables and indices separately *************************

BAR............................................... 42030       21.5% 
FOO............................................... 42030       21.5% 
BARINDEX.......................................... 40410       20.7% 
FOOINDEX.......................................... 40410       20.7% 
SQLITE_AUTOINDEX_FOO_1............................ 30206       15.5% 
SQLITE_MASTER..................................... 1            0.0% 
SQLITE_STAT1...................................... 1            0.0% 


-----Original Message-----
From: sqlite-users [mailto:sqlite-users-boun...@mailinglists.sqlite.org] On 
Behalf Of Simon Slavin
Sent: Wednesday, March 01, 2017 4:38 PM
To: SQLite mailing list
Subject: Re: [sqlite] Non-unique columns in unique index


On 1 Mar 2017, at 9:11pm, Deon Brewis <de...@outlook.com> wrote:

> "But look how much space and processing time it would take up"
> 
> Can you clarify what you mean by "space" ?

Your triggers have to be stored.  Every time they’re activated (each time you 
insert a row) they have to be run.  That requires SQLite to grab enough memory 
and/or disk space to run the triggers and to construct any temporary indexes 
they require.

Also, it’s a nightmare to debug in comparison with just creating one extra 
index.

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

Reply via email to