Hi,

I have read that INSERT OR REPLACE is equivalent to a DELETE followed by an
INSERT. I came across a case where that is not true.

Set up a test case

    $ rm -f asdf.sqlite && sqlite3 asdf.sqlite

    sqlite> .mode columns
    sqlite> .headers on
    sqlite> PRAGMA foreign_keys=OFF;

    sqlite> CREATE TABLE users (
       ...>     id INT UNSIGNED NOT NULL PRIMARY KEY,
       ...>     username TEXT NOT NULL
       ...> );

    sqlite> CREATE TABLE user_extras (
       ...>     user_id INT UNSIGNED NOT NULL PRIMARY KEY,
       ...>     other INT NULL DEFAULT NULL,
       ...>     FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE
CASCADE ON DELETE CASCADE
       ...> );

    sqlite> INSERT INTO users (id, username) VALUES (1, 'asdf');

    sqlite> INSERT INTO user_extras (user_id, other) VALUES (1, 33);

    sqlite> SELECT * FROM users;
    id          username
    ----------  ----------
    1           asdf

    sqlite> SELECT * FROM user_extras;
    user_id     other
    ----------  ----------
    1           33


Let's try a delete followed by an insert.

    sqlite> DELETE FROM users WHERE id = 1;

    sqlite> SELECT * FROM users;

    sqlite> SELECT * FROM user_extras;
    user_id     other
    ----------  ----------
    1           33

    sqlite> INSERT INTO users (id, username) VALUES (1, 'asdf');

    sqlite> SELECT * FROM users;
    id          username
    ----------  ----------
    1           asdf

    sqlite> SELECT * FROM user_extras;
    user_id     other
    ----------  ----------
    1           33

Notice that the value `user_extras.other` is still 33. That's good.

    sqlite> INSERT OR REPLACE INTO users (id, username) VALUES (1, 'asdf');

    sqlite> SELECT * FROM users;
    id          username
    ----------  ----------
    1           asdf

    sqlite> SELECT * FROM user_extras;
    user_id     other
    ----------  ----------
    1           33

Still 33. That's good.

Based on the above, it looks like `INSERT OR REPLACE` is equivalent to a
`DELETE` followed by an `INSERT`.

If we add a trigger, things change...

    sqlite> CREATE TRIGGER users_after_insert AFTER INSERT ON users
       ...> BEGIN
       ...>     INSERT OR IGNORE INTO user_extras (user_id) VALUES (new.id);
       ...> END;

A `DELETE` followed by an `INSERT` still works as before.

    sqlite> DELETE FROM users WHERE id = 1;

    sqlite> SELECT * FROM users;

    sqlite> SELECT * FROM user_extras;
    user_id     other
    ----------  ----------
    1           33

    sqlite> INSERT INTO users (id, username) VALUES (1, 'asdf');

    sqlite> SELECT * FROM users;
    id          username
    ----------  ----------
    1           asdf

    sqlite> SELECT * FROM user_extras;
    user_id     other
    ----------  ----------
    1           33

See above that the value of `user_extras.other` survived the `DELETE`
followed by the `INSERT`.

When we use `INSERT OR REPLACE` the `user_extras.other` value is cleared
out.

    sqlite> INSERT OR REPLACE INTO users (id, username) VALUES (1, 'asdf');

    sqlite> SELECT * FROM users;
    id          username
    ----------  ----------
    1           asdf

    sqlite> SELECT * FROM user_extras;
    user_id     other
    ----------  ----------
    1

Because of the trigger, `INSERT OR REPLACE` is no longer equivalent to
`DELETE` followed by `INSERT`.


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

Reply via email to