ibm1620 has asked for the wisdom of the Perl Monks concerning the following question:
This test program shows how I've tried to do it:
Running produces this output:#!/usr/bin/env perl use v5.38; use DBI; my $memory; # = ':memory:'; my $dbname = $memory // '/Users/chap/private/data/music'; my $dbh = create_database(); # insert some songs. watch out for dupes. for my $song (qw{ Alpha Beta Gamma Alpha Delta Omicron }) { my $rowid = insert_unique( 'Songs', 'song', $song ); say "Inserting '$song', got rowid=$rowid"; } sub create_database() { unlink $dbname unless $memory; my $dbms = 'SQLite'; my $dbname = $dbname; my $user = ''; my $passwd = ''; my $dbh = DBI->connect( "DBI:$dbms:dbname=$dbname", $user, $passwd, { RaiseError => 1, AutoCommit => 1 } ) or die "Cannot connect: $DBI::errstr"; my $songs = <<~'EOF'; CREATE TABLE Songs (id INTEGER PRIMARY KEY, song VARCHAR(40) UNIQUE ) EOF $dbh->do($songs); $dbh->do("CREATE INDEX ix_song ON Songs (song)"); return $dbh; } # sub which conditionally inserts a unique value, and returns rowid sub insert_unique( $table, $column, $value ) { state %sths; # cache my prepared statement handles if ( !exists $sths{$table} ) { $sths{$table}{SELECT} = $dbh->prepare("SELECT id FROM $table WHERE $column = (?)") +; $sths{$table}{INSERT} = $dbh->prepare("INSERT INTO $table ($column) VALUES (?)"); } my $rowid = $sths{$table}{SELECT}->execute($value) || die; say "Value='$value': attempt to select yielded rowid=$rowid"; if ( $rowid == 0 ) { say "So value needs to be inserted."; $sths{$table}{INSERT}->execute($value); $rowid = $dbh->do("SELECT last_insert_rowid()"); } else { say "So value has been previously inserted."; } return $rowid; }
Several things were not as expected: executing 'SELECT id...' returned 0e0 even when the value exists in the table (as in adding Alpha the 2nd time). And when the value needed to be inserted, the call to `last_insert_rowid()` always returned 1, rather than the most recently inserted rowid.Value='Alpha': attempt to select yielded rowid=0E0 So value needs to be inserted. Inserting 'Alpha', got rowid=1 Value='Beta': attempt to select yielded rowid=0E0 So value needs to be inserted. Inserting 'Beta', got rowid=1 Value='Gamma': attempt to select yielded rowid=0E0 So value needs to be inserted. Inserting 'Gamma', got rowid=1 Value='Alpha': attempt to select yielded rowid=0E0 So value needs to be inserted. DBD::SQLite::st execute failed: UNIQUE constraint failed: Songs.song a +t /Users/chap/private/perl/insert_unique line 51. DBD::SQLite::st execute failed: UNIQUE constraint failed: Songs.song a +t /Users/chap/private/perl/insert_unique line 51.
The following shows that the data was inserted into the table:
In addition to being baffled why this isn't working as I expected, I also wonder if there isn't a simpler way to accomplish this that I haven't thought of. I would imagine that this is a common situation when loading a normalized database.$ sqlite3 music SQLite version 3.43.2 2023-10-10 13:08:14 Enter ".help" for usage hints. sqlite> select * from Songs; 1|Alpha 2|Beta 3|Gamma
Humbly yours,
1620
|
---|