New to SQLite, very rusty on DBI and SQL. I'm trying to build a table of unique values, and I need to know their rowids so that other tables can refer to these values, and bring them together with a SELECT .. JOIN. Every time I store a value in the table, I need to first check to see if the value already exists -- if so, I need to obtain its rowid; if not, I need to insert it and then obtain its rowid.

This test program shows how I've tried to do it:

#!/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; }
Running produces this output:
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.
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.

The following shows that the data was inserted into the table:

$ 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
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.

Humbly yours,

1620


In reply to SQLite: INSERT into a unique column and retrieve rowid by ibm1620

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.