REPLACE INTO sqlite doesn't replace - objective-c

got a weird problem. I have a sqlite table in my objective-c app:
NSString *sql = #"CREATE TABLE IF NOT EXISTS user_results (id INTEGER PRIMARY KEY ASC AUTOINCREMENT, gameID INTEGER UNIQUE, gameDesc TEXT, result INTEGER)";
Then I execute query:
[[DatabaseController getInstance].resultsDB executeUpdate:#"REPLACE INTO user_results (gameID, gameDesc, result) VALUES (?, ?, ?)",[NSNumber numberWithInt:self.gameID],[test JSONRepresentation],[NSNumber numberWithInt:sumBalls]];
But the problem is that it doesn't replace the row with the same gameID, it just adds one (even though it's UNIQUE), any ideas why would it happen?
P.S. I'm using FMDB to work with sqlite.
Thanks in advance.
Solution: Had to use [NSNumber numberWithInt:self.gameID].integerValue instead of [NSNumber numberWithInt:self.gameID] when sending to sql query.

Has your schema changed, with the UNIQUE constraint added later? Your schema & SQL should work as expected. I just tried this and it works fine:
sqlite3
CREATE TABLE IF NOT EXISTS user_results (id INTEGER PRIMARY KEY ASC AUTOINCREMENT, gameID INTEGER UNIQUE, gameDesc TEXT, result INTEGER);
insert into user_results values (1,1,'hi', 1); --insert 2 test rows
insert into user_results values (2,2,'2', 2);
select * from user_results;
1|1|hi|1
2|2|2|2
Now an insert fails:
insert into user_results values (3,1,'1', 1);
Error: column gameID is not unique
REPLACE INTO does what you expect:
replace into user_results (gameid, result) values (2, 3);
select * from user_results;
1|1|hi|1
3|2||3
It deleted the row with id 2, and replaced it with a new row id 3 and gameid 2. Unless you were expecting it to replace the primary key=2 row? What Sqlite does is delete any prior rows that would cause violation of the unique key, then inserts a new row. See http://www.sqlite.org/lang_conflict.html. Note it didn't add an EXTRA row. It deleted one and added one (in other words, 'replaced' :)
If your replace into SQL included the id column, that would work, here I'm effectively updating the row with id 3. Of course you'd have to figure out the id of the row you wanted to replace...
replace into user_results values (3,3,'2', 2);
select * from user_results;
1|1|hi|1
3|3|2|2
Is the id column something you really care about? Sqlite will create such a column for you anyway.

The gameID field is not the PRIMARY KEY, ID is. As you have it, the REPLACE INTO will only work with the ID field. I recommend making the gameID field the primary key to get the result that you are looking for.
NSString *sql = #"CREATE TABLE IF NOT EXISTS user_results (gameID INTEGER PRIMARY KEY, gameDesc TEXT, result INTEGER)";

Related

Update Postgres SQL table with SERIAL from previous insert [duplicate]

This question already has answers here:
Insert a value from a table in another table as foreign key
(3 answers)
Closed 4 months ago.
Very new to SQL in general, working on creating 2 Tables, 1 for example representing appliances with a primary key, second representing a microwave for example with its FK referencing the primary tables PK.
I'm using SERIAL as the id for the primary table, but don't know how to update or insert into the second table using that specific generated value from the first.
I've created my tables using PSQL (Postgres15) like so:
CREATE TABLE Appliances (
id SERIAL NOT NULL,
field1 integer NOT NULL DEFAULT (0),
--
PRIMARY KEY (id),
UNIQUE(id)
);
CREATE TABLE Microwaves (
id integer NOT NULL,
field1 integer,
--
PRIMARY KEY (id),
FOREIGN KEY (id) REFERENCES Appliances(id)
);
Inserting my first row into the Appliance table:
INSERT INTO Appliances(field1) VALUES(1);
SELECT * FROM Appliances;
Yields:
And a query I found somewhere pulls the current increment of the SERIAL:
SELECT currval(pg_get_serial_sequence('Appliances', 'id'));
Yields:
I'm struggling to determine how to format the INSERT statement, have tried several variations around the below input:
INSERT INTO Microwaves VALUES(SELECT currval(pg_get_serial_sequence('Appliances', 'id'), 1));
Yields:
Appreciate feedback on solving the problem as represented, or a better way to tackle this in general.
Okay looks like I stumbled on at least one solution that works in my case as taken from https://stackoverflow.com/a/50004699/3564760
DO $$
DECLARE appliance_id integer;
BEGIN
INSERT INTO Appliances(field1) VALUES('appliance2') RETURNING id INTO appliance_id;
INSERT INTO Microwaves(id, field2) VALUES(appliance_id, 100);
END $$;
Still open to other answers if this isn't ideal.

PostgreSQL- insert result of query into exisiting table, auto-increment id

I have created an empty table with the following SQL statement. My understanding (based on this tutorial: https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-serial/) was that SERIAL PRIMARY KEY will automatically provide an auto-incremented id for every new row:
CREATE TABLE "shema".my_table
(
id SERIAL PRIMARY KEY,
transaction text NOT NULL,
service_privider text NOT NULL,
customer_id text NOT NULL,
value numeric NOT NULL
)
WITH (
OIDS = FALSE
);
ALTER TABLE "shema".my_table
OWNER to admin;
Now I am querying another tables and would like to save the result of that query into my_table. The result of the query outputs following schema:
transaction
service_provider
customer_id
value
meaning the schema of my_table minus id. when I try to execute:
INSERT into my table
Select {here is the query}
Then I am getting an error that column "id" is of type integer but expression is of type text. I interpret it that the sql query is looking for id column and cannot find it. How can I insert data into my_table without explicitly stating id number but have this id auto-generated for every row?
Always mention the columns you want to INSERT:
INSERT INTO schemaname.my_table("transaction", service_privider, customer_id, value)
SELECT ?, ?, ?, ?;
If you don't, your code will break now or somewhere in the future.
By the way, transaction is a reserved word, try to use a better column name.

How to efficiently insert ENUM value into table?

Consider the following schema:
CREATE TABLE IF NOT EXISTS snippet_types (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS snippets (
id INTEGER NOT NULL PRIMARY KEY,
title TEXT,
content TEXT,
type INTEGER NOT NULL,
FOREIGN KEY(type) REFERENCES snippet_types(id)
);
This schema assumes a one-to-many relationship between tables and allows efficiently maintaining a set of ENUMs in the snippet_types table. Efficiency comes from the fact that we don't need to store the whole string describing snippet type in the snippets table, but this decision also leads us to some inconvenience: upon inserting we need to retrieve snippet id from snippet_types and this leads to one more select and check before inserting:
SELECT id FROM snippet_types WHERE name = "foo";
-- ...check that > 0 rows returned...
INSERT INTO snippets (title, content, type) values ("bar", "buz", id);
We could also combine this insert and select into one select like that:
INSERT INTO snippets (title, content, type)
SELECT ("bar", "buz", id) FROM snippet_types WHERE name = "foo"
However, if "foo" type is missing in snippet_types then 0 rows would have been inserted and no error returned and I don't see a possibility to get a number of rows sqlite actually inserted.
How can I insert ENUM-containing tuple in one query?

Converting PL/SQL code to SQLite

I would like to know how can I convert the following block of Oracle PL/SQL code into SQLite so that it can be used in an Objective C program:
SELECT CUSTOMERS_ID_SEQ.NEXTVAL
INTO V_CUSTOMER_ID
FROM DUAL;
where V_CUSTOMER_ID is CUSTOMER_ID%TYPE NOT NULL, and
CUSTOMER_ID is integer type in table.
SQLite does not have sequences.
To get an autoincrementing ID, you have to use an INTEGER PRIMARY KEY column.
CL is right.
Short answer: A column declared INTEGER PRIMARY KEY will autoincrement.
Long answer: If you declare a column of a table to be INTEGER PRIMARY KEY, then whenever you insert a NULL into that column of the table, the NULL is automatically converted into an integer which is one greater than the largest value of that column over all other rows in the table, or 1 if the table is empty. (If the largest possible integer key, 9223372036854775807, then an unused key value is chosen at random.) For example, suppose you have a table like this:
CREATE TABLE t1(
a INTEGER PRIMARY KEY,
b INTEGER
);
With this table, the statement
INSERT INTO t1 VALUES(NULL,123);
is logically equivalent to saying:
INSERT INTO t1 VALUES((SELECT max(a) FROM t1)+1,123);
There is a function named sqlite3_last_insert_rowid() which will return the integer key for the most recent insert operation.
Check the following for FAQ help on SQLite: http://sqlite.org/faq.html#q1

How not to insert specific value into database

I have MS SQL Server database and insert some values to one of the tables.
Let's say that the table contains columns ID, int Status and text Text.
If possible, I would like to create a trigger which prevents from writing specific incorrect status (say 1) to the table. Instead, 0 should be written. However, other columns should be preserved when inserting new values:
If the new row written is (1, 4, "some text"), it is written as is.
If the new row written is (1, 1, "another text"), it is written as (1, 0, "another text")
Is it possible to create such trigger? How?
EDIT: I need to allow writing such record even if status column is invalid, so foreign keys will not work for me.
I think you would need a foreign key to ensure data integrity even if you choose to use a trigger (though I would myself prefer a 'helper' stored proc -- triggers can cause debugging hell) e.g.
CREATE TABLE MyStuff
(
ID INTEGER NOT NULL UNIQUE,
Status INTEGER NOT NULL
CHECK (Status IN (0, 1)),
UNIQUE (Status, ID)
);
CREATE TABLE MyZeroStuff
(
ID INTEGER NOT NULL,
Status INTEGER NOT NULL
CHECK (Status = 0),
FOREIGN KEY (Status, ID)
REFERENCES MyStuff (Status, ID),
my_text VARCHAR(20) NOT NULL
);
CREATE TRIGGER tr__MyZeroStuff
ON MyZeroStuff
INSTEAD OF INSERT, UPDATE
AS
BEGIN;
INSERT INTO MyZeroStuff (ID, Status, my_text)
SELECT i.ID, 0, i.my_text
FROM inserted AS i;
END;
An insert trigger has been mentioned, but another way to achieve this is to have a foriegn key on your Status column which points back to a Status table - this will not allow the write and change the value, instead it will simply disallow the write if the foriegn key is not valid.
Check out referential integrity for more info on this option.