Using SQLite triggers to increment a count
I want to apply tags to urls and keep a count of how many times each tag has been used. Here’s some code that does just that:
use DBI; use Text::Table; my $FILENAME = 'play.sqlite'; unlink $FILENAME; my $dsn = "dbi:SQLite:database=$FILENAME"; my $dbh = DBI->connect($dsn) or die; $dbh->do($_) or die "$DBI::errstr for $_" for split /^=+$/m, join '', ; my $sql = "insert into tags (tag) values(?)"; my $sth = $dbh->prepare($sql) or die; $sth->execute($_) or die for qw/one two three/; $sql = "insert into urls (url) values(?)"; $sth = $dbh->prepare($sql) or die; $sth->execute("site $_") or die for qw/a b c d e f/; $sql = "insert into tagged (url_id, tag_id) values(?, ?)"; $sth = $dbh->prepare($sql) or die; $sth->execute(@$_) or die for [2,1], [3,1], [1,2]; for my $t (qw/urls tags tagged/) { say "\n=== \U$t ==="; $sth = $dbh->column_info(undef, undef, $t, undef); my @cols; while (my $c = $sth->fetchrow_hashref) { push @cols, $c->{COLUMN_NAME}; } my $tab = Text::Table->new(@cols); $sth = $dbh->prepare("select * from $t"); $sth->execute; while (my $c = $sth->fetchrow_arrayref) { $tab->add(@$c) } print $tab; } __DATA__
And, here is the SQL that goes in the __DATA__
section:
CREATE TABLE tags ( id INTEGER PRIMARY KEY, tag TEXT NOT NULL UNIQUE, count INTEGER DEFAULT 0 ); === CREATE TABLE urls ( id INTEGER PRIMARY KEY, url TEXT NOT NULL UNIQUE ); === CREATE TABLE tagged ( id INTEGER PRIMARY KEY, tag_id INTEGER REFERENCES tags(id), url_id INTEGER REFERENCES urls(id), unique( tag_id, url_id ) ); === CREATE TRIGGER inc_tag_count AFTER INSERT ON tagged BEGIN UPDATE tags SET count = count + 1 WHERE id = new.tag_id; END; === CREATE TRIGGER dec_tag_count AFTER DELETE ON tagged BEGIN UPDATE tags SET count = count - 1 WHERE id = new.tag_id; END;
This results in this output
=== URLS === id url 1 site a 2 site b 3 site c 4 site d 5 site e 6 site f === TAGS === id tag count 1 one 2 2 two 1 3 three 0 === TAGGED === id tag_id url_id 1 1 2 2 1 3 3 2 1