Android 2.2 + SQLite + Defining referential integrity constraints - android

I'm developing an Android 2.2+ app with SQLite db support. I'm trying to define referential integrity constraints while creating the tables. I've also enabled the foreign key support by executing db.execSQL( "PRAGMA foreign_keys=ON;" ); in onCreate and onOpen methods of SQLiteOpenHelper. Afteer setting up my tables and proper foreign key references, it still allows me to insert rows in tables where reference records are missing. For e.g. I've following structure
CREATE TABLE Questions(_id integer primary key,question text not null);
CREATE TABLE Ques_Ans(_id integer primary key autoincrement,qid integer not null,
aid integer not null,is_correct integer default 0,
FOREIGN KEY (qid) REFERENCES Questions(_id));
and following my data in the table
INSERT INTO Questions VALUES(1, 'Some text');
INSERT INTO Ques_Ans(qid, aid, is_correct) VALUES(20, 1, 0);
If foreign key is properly set on Ques_Ans table, 2nd insert should have failed as there is no record in Questions table with id 20, but somehow my app does not thrown any error and inserts the 2nd insert statement. Can anybody tell me whats wrong over here or am I missing any configuration over here?
Update [03-Mar-2012]: After discussion thread with #Catcall
Using sqlite3 tool, switching on PRAGMA foreign_keys=ON; foreign key works as expected
Same thing if used via app on Emulator or on Phone does not work
Insert statements executed using insert() or execSQL(). None of them throw foreign key constraint failed error
PRAGMA integrity_check returns ok. So database is not corrupt.

Your foreign key is on the column "qid", not on the column "_id".
This INSERT statement
INSERT INTO Ques_Ans VALUES(20, 1, 0);
should have thrown the error
Error: table Ques_Ans has 4 columns but 3 values were supplied
This INSERT should succeed.
INSERT INTO Ques_Ans VALUES(20, 1, 0, 0);
This one should fail.
INSERT INTO Ques_Ans VALUES(21, 2, 0, 0);
Error: foreign key constraint failed
Later . . .
Since it works in sqlite, but not in your emulator, the problem is probably in either the emulator or your code.
This is the idiom for database transactions.
db.beginTransaction();
try {
...
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Android docs suggest using insert() instead of execSQL(). Make sure you're trapping, catching, or checking for errors in every function that can return an error. So, for example, if you switch to insert(), check its return value for -1.
If that doesn't help, you might try tagging this question with "Java" (for example), or asking a new question that focuses on your code and on catching errors.

IN SQLite Foreign key constraints are disabled by default (for backwards compatibility). You have to enable it explicitly using
PRAGMA foreign_keys = 1
after you establishing your connection with the database. Here's the link to the official docs that explains it in more depth. http://sqlite.org/foreignkeys.html Please navigate to enabling foreign key support in the above link.

Related

Copying an SQLite Table Applying Default Values

I have an SQLite database in my Android app that I would like to update. The update requires a change of datatype from INT to FLOAT. I understand the way to achieve this in SQLite is to create a new table, copy the data, and then replace the old table with the new one. Copying would be done as follows:
INSERT INTO newTable SELECT * FROM oldTable
where integer values would be conveniently converted into floating point values.
Now, there is the problem that another, previously optional column is now ´NOT NULL´ but has a default value. I would like to have all NULL instances replaced by the default as part of the above process using the following statement:
INSERT INTO newTable SELECT * FROM oldTable ON CONFLICT REPLACE
or, for completeness, in Android/Kotlin:
db.execSQL(“INSERT INTO newTable SELECT * FROM oldTable ON CONFLICT REPLACE”)
This gives me a syntax error, and I could not find a suitable example elsewhere. What would be the correct syntax to achieve this - and is what I'm trying to do even possible?
If you look at the documentation for INSERT, you'll see it should be INSERT OR REPLACE ..., and, yes, it'll work to replace null values in the not-null column with the default value.
Replace conflict resolution documentation:
When a UNIQUE or PRIMARY KEY constraint violation occurs, the REPLACE algorithm deletes pre-existing rows that are causing the constraint violation prior to inserting or updating the current row and the command continues executing normally. If a NOT NULL constraint violation occurs, the REPLACE conflict resolution replaces the NULL value with the default value for that column, or if the column has no default value, then the ABORT algorithm is used. If a CHECK constraint or foreign key constraint violation occurs, the REPLACE conflict resolution algorithm works like ABORT.
Example:
sqlite> CREATE TABLE foo(x, y NOT NULL DEFAULT 1);
sqlite> INSERT INTO foo(x,y) VALUES ('a', null);
Error: NOT NULL constraint failed: foo.y
sqlite> INSERT OR REPLACE INTO foo(x,y) VALUES ('a', null);
sqlite> SELECT * FROM foo;
x y
---------- ----------
a 1

Does sqlite "insert or replace" work with AUTOINCREMENT primary key?

I have a database in which i have created one table with name "SectionDetails".
In this table i have set "id" as a primary key with AUTOINCREMENT property.And i am inserting my data into this table.
However, i came to the scenario where i need to check if record i am inserting is already present or not(if record is present then replace it with same values or skip it,And if record is not present then insert new one ).
But when i tried to insert record with same column values, it increases the primary key and insert the same row again instead of replacing.
So, my question is-
Does sqlite "insert or replace" works with AUTOINCREMENT primary key?
I am using following query:
insert or replace into SectionDetails(Name,Month,Title,Url)values('Deepak',"August","None","www.dd619.com")
here column "id" is not appearing because its a primary key with AUTOINCREMENT property.
You will need to add some unique constraints to your other columns to make this work and even then you will have your IDs change.
insert or replace is really an insert with on conflict replace conflict resolution strategy. That is, when the insert would violate some constraint, all conflicting rows are first deleted and the insert takes place only then. The autoincrement mechanism will then generate a new ID value for you.
For more information: http://www.sqlite.org/lang_conflict.html
Short answer: no it does not.
The "insert or replace" only works well when you also specify the primary key. With the autoincrement in a setup that you describe, this cannot work.
Probably the quickest way to do that is by creating a custom insert/update method for your use case.

data duplication in sqlite with android app

i am getting information from user in sqlite database.
But when i insert same record which is already in database it is added again.
how i can stop duplication of record in sqlite. I am developing this in android.
I am using mobile number as primary key. still it add that record in database.
Please suggest me appropriate solutions.
Thanks in advanced.
Be aware of the limitations of REPLACE or INSERT OR REPLACE as these will overwrite any custom data your app user has added to these rows in the database - it is not as advanced as UPSERT in other SQL databases.
As mentioned in a previous post you really need to identify what the primary key could be and use this information to either update old data or to remove an old row before inserting the fresh one.
If this is not possible then you could always DELETE FROM my_table or DROP my_table before running the insertions so that there will be no duplicates. This will (for better or worse) also make sure that data that is missing from new imports is not left lying around in your app.
make sure you have set your phone number as Primary Key at the time you created the table.
for example:
String query = "CREATE TABLE IF NOT EXISTS PhoneBook ("+
"TelNum VARCHAR(100) PRIMARY KEY NOT NULL,"+
"Address TEXT);";
db.execSQL(query);
and in case you want to enforce foreign keys defined in your table then call the following method before doing anything in your database
db.execSQL("PRAGMA foreign_keys = ON;"); //enforcing FK
Use REPLACE INTO keyword:
REPLACE INTO my_table (pk_id, col1) VALUES (5, '123');
This automatically identifies the primary key and finds a matching row to update, inserting a new one if none is found.

Conflict resolution in Android's SQLiteDatabase

How does Android's SQLite library resolve conflicts if I insert duplicate rows? I am building my app for API level 7, which unfortunately does not have the SQLiteDatabase.insertOnConflict() method that was included starting in API level 8.
You can specify a UNIQUE index in the table definition which will allow rows to be REPLACED:
CREATE TABLE mytable (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
UNIQUE (id) ON CONFLICT REPLACE
)
If a row an INSERT or UPDATE statement tries to add a row with an id which already exists, the existing row is replaced with the new one.
There's a ON CONFLICT clause in SQLite that you can say INSERT INTO ... ON CONFLICT....
Read the documentation please. http://www.sqlite.org/lang_conflict.html
By default i you have unique index exception will be thrown on duplicated index insert. Common solution is check record existence before execute insert - pseudo-code:
select * from people where name = 'Smith';
//if resultset is empty then perform insert
insert into people values 'Smith'

SQLite triggers in Android?

I want to force a foreign key constarint on a table in an Android application.
I've searched that this can be done by using triggers:
I did it like this:
db.execSQL("CREATE TRIGGER dept_id_trigger22+" +
" AFTER INSERT "+
" OF EmployeeName ON Employees"+
" BEGIN"+
//Condition
" RAISE(ABORT,'error') END;");
but no error was raised and the illegal values are inserted.
what is wrong with this ?
Ok I got it
Android supports SQLite triggers.
The correct syntax is
db.execSQL("CREATE TRIGGER dept_id_trigger22" +
" AFTER INSERT "+
"ON Employees"+
" BEGIN"+
//Condition
" SELECT RAISE(ABORT,'error'); END;");
I forgot to add semicolon after the raise statement.
This does not execute the statement but it does not throw an exception.
still will search for how to throw exceptions
thanks
Foreign keys are only supported on Android on Froyo (2.2) or newer, for previous versions you can include them but SQLite ignores them. All Android versions of SQLite support triggers to produce the same effect though.
Newer versions of SQLite (for your PC) has a command called "genfkey" that will analyze your SQLite database (which has foreign keys in it) and produce the equivalent triggers. This way you can design your tables with foreign key constraints while also supporting all versions of the OS.
On Windows, open the SQLite command line tool with your database file as a parameter:
sqlite3 mydatabase.db
.genfkey --exec
This will generate triggers for all of your key constraints.
I don't expect any votes for this answer, just to let you know:
You could use another database, for example the H2 database. Disclaimer: I'm the main author of H2.
There are some disadvantages: some (not all) operations are slower, for example opening and closing a database. The jar file is relatively big (about 1 MB). You would have to use the JDBC API.
But the advantage is: H2 supports using triggers, constraints, and so on.
To delete Last 50 rows when count is greater than 100
sqliteDB.execSQL("CREATE TRIGGER IF NOT EXISTS delete_trigger
AFTER INSERT ON table1
WHEN (SELECT COUNT(*) FROM table1) > 50 " +
BEGIN
delete From table1 where id not in(select id from table1 order by id desc limit 100;
END;"
);
I discovered that the SQLite version used does not support foreign keys - so I expect that triggers are not supported, too.

Categories

Resources