Create updatable VIEW from two tables with Triggers - android

I'm creating an android application that uses from sqlite database and ...
I have problem with logic of my database. I have two tables as below:
TBL_First
id (int), first_name (text), first_qty (int)
TBL_Second
id (int), second_name (text), second_qty (int)
Each table will be used in separate activity.
Qty columns of each table has 0 default value and users can change it at run-time.
Also, I have an final activity that will shows items of TBL_First & TBL_Second where qty columns
of these tables are bigger than 0.
I've use TBL_Final for my new activity as below:
INSERT INTO "TBL Final"(final_name, final_qty)
SELECT first_name,first_qty FROM "TBL_First" WHERE first_qty > 0
UNION ALL
SELECT second_name,second_qty FROM "TBL_Second" WHERE second_qty > 0
I can fetch data of TBL_final and show its data to users, but if users want to update final_qty, first_qty or
second_qty doesn't update!
I think that I can use VIEW instead of TBL_Final, but I know that it would not be updated!
I searched around this issue and found out that Triggers can be helpful.
Unfortunately, I'm not familiar with VIEW and Trigger at all !
Just I know I can use VIEW as below but I don't know how can I use Triggers to update view_qty
and first_qty and second_qty !!!
create or replace view final_view (view_name,view_qty) as
select first_name,first_qty where first_qty > 0
union all
select second_name,second_qty where second_qty > 0
Any suggestions would be appreciated ...

The trigger has access to the old and new values of the view's row with the OLD and NEW table aliases; it must use them to look up the row(s) of the base tables:
CREATE TRIGGER final_view_update
INSTEAD OF UPDATE OF view_qty ON final_view
FOR EACH ROW
BEGIN
UPDATE first_qty
SET first_qty = NEW.view_qty
WHERE first_name = NEW.view_name;
UPDATE second_qty
SET second_qty = NEW.view_qty
WHERE second_name = NEW.view_name;
END;
The trigger always executes two UPDATEs, but only one WHERE clause will match.

Related

set the default value of the new column to a value of an existing column after altering table

I have a table called "users".
id name
1 jack
2 lisa
I want to add a new column and set the default value of it to the value of the "id" column.
ALTER TABLE users ADD COLUMN user_index INTEGER NOT NULL DEFAULT id;
since, the DEFAULT keyword, only accepts constant values the above code doesn't work.so, how can I set the default value of the new column to the value of "id" column?
This can't be done with just the ALTER statement. I would recommend performing the alteration and a copy in a transaction, where the copy would look something like:
UPDATE tableName SET user_index = id;
I believe that the following demo SQL will do what you want.
/*
Create and populate inital table
*/
DROP TRIGGER IF EXISTS set_user_index_as_id;
DROP TABLE IF EXISTS users; -- Note will drop the trigger
CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, `name` TEXT);
INSERT INTO users (`name`) VALUES('jack'),('lisa');
-- Show all columns of all rows of the initial table
SELECT * FROM users;
-- Add the new column with default value of -1 (should not interfere with id assuming it is never -1)
-- ONE-OFF SCHEMA CHANGE
-- Note sets default for rows previously added to default
ALTER TABLE users ADD COLUMN user_index INTEGER NOT NULL DEFAULT -1;
-- Show all columns of all rows after the alter
SELECT * FROM users;
-- Add the trigger to adjust the user_index value whenever a row is inserted
CREATE TRIGGER IF NOT EXISTS set_user_index_as_id AFTER INSERT ON users
BEGIN
-- update rows that have not been updated i.e. user_index is -1
-- (so will adjust the original rows)
UPDATE users SET user_index = id WHERE user_index = -1;
END
;
-- Add some more rows (i.e.test the above)
INSERT INTO users ('name') VALUES('fred'),('mary');
-- Show Show all columns of all rows after the inserts
SELECT * FROM users;
This sets the default value to -1 (assuming that id will never be -1, it could be another value that id would never be that value) when ALTERing the table to add the column. A TRIGGER is then added that will update any rows that have -1 in the user_index column to be the same value as the id column whenever a new row is inserted.
as all rows with -1 are updated the first insert after the trigger has been added will update any previously inserted rows (the default value is retrospectively applied to old rows)
Note The above is a demo and has been designed to rerun, hence the DROP statements.
When run the queries, that show the progress, are :-
After initial inserts
After the Alter
After the new inserts
The above assumes that id is an alias of the rowid (what some term as AUTOINCREMENT) and hence the rows are in monotonically increasing order (increase from 1 to 2 to 3 ....).
However, say the first two rows had id values of 10 and 50 and then the new rows 51 and 52. These out of sequence values would be applied. A test very similar to the above, just changing the first insert to INSERT INTO users VALUES(10,'jack'),(50,'lisa');
has the final result, as expected, of :-

SQLite (Android) : UPDATE query with ORDER BY

Android, SQLite : I want to insert rows in between other rows in myTable using SQLite in android. For this, I am trying to increment ids of the all rows starting say row 3. So that I can insert a new row at position 3.
The primary key of myTable is column id. There are no other constraints in the table.
I have tried using the query mentioned in https://stackoverflow.com/a/9177264/6671004. This query does work in mySQL but not in Android (SQLite)
Here's the line of code :
database.execSQL("UPDATE myTable SET id = (id + 1) where id > 2 ORDER BY id desc");
Here's the error I'm getting on Android Studio (Compile time) :
https://imgur.com/a/9r0iyAa
This is the exception I'm getting if I remove 'ORDER BY id DESC' from the query :
java.lang.RuntimeException: Unable to start activity ComponentInfo{...}: android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: myTable.id (code 1555)
Is this the correct way to do this? Or is there a better way?
As pointed out by many, this is definitely not the correct way to go.
But I found workaround in case someone else is looking for a similar implementation.
Here it is :
UPDATE myTable SET id = - (id + 1) WHERE id > 1;
UPDATE myTable SET id = - id WHERE id < 0;
This is a hack which I found here.
Again, this is not the correct way to go. But just posting a working solution I found.
I have tried using the query mentioned in
https://stackoverflow.com/a/9177264/6671004. This query does work in
mySQL but not in Android (SQLite)
That question is tagged MYSQL. MYSQL has many differences from SQLite.
Here's the line of code :
database.execSQL("UPDATE myTable SET id = (id + 1) where id > 2 ORDER
BY id desc");
The SQLite UPDATE SQL takes the form of :-
i.e. there is no ORDER BY clause and hence the error saying that if you're going to use any KEYWORD then it must be a BETWEEN or IN or a ; (of course you could also extend the condition using AND OR and so on).
This is the exception I'm getting if I remove 'ORDER BY id DESC' from
the query :
The reason being is that the rowid (id being an alias of rowid) has an implied UNIQUE constraint and that the rows will be updated according to the id column in order. So if there are more than 3 rows (or have been and the last row has never been deleted) then when the id is 3, a row with 4 will exist and 3 + 1 = 4 so the row already exists and hence the UNIQUE constraint being encountered.
I want to insert rows in between other rows in myTable using SQLite in
android. For this, I am trying to increment ids of the all rows
starting say row 3. So that I can insert a new row at position 3.
In short that is not a good idea and is virtually definitely not needed.
Is this the correct way to do this? Or is there a better way?
Definitely no
At a guess you want a nice humanly understandable value so you can know what's going on. For example you may present a list with the sequence so you can then say delete the row that has a sequence of 3 and thus equate that to the id column. Fine until you present the list in a different order which may be more user friendly. Suddenly your sequence becomes complicated or even useless and if displayed confusing.
identifiers are intended to identify a row and allow fast access to that row as a numerical index will be more efficient (than a human easily readable non-numeric index) to process. They also cater for reducing unnecessary additional processing (shifting data).
An efficient methodology is presenting the list with tying the id to the position in the list (so position could be the nth element of an array that holds the respective id, regenerating the list (the underlying array) according to a different order so the nth element will still have the respective id).
Embarking on a process of shifting rows will impose additional resource usage for example extra disk IO whose cost is relatively expense. This will be compounded by the fact that you will have to process the rows in reverse order to get around the UNIQUE constraint, that in itself is going to require using even costlier methods because SQLite will by default try to perform the updates efficiently rather than cater for the efficiencies required to digress from recognised/accepted techniques that utilise the underlying efficiencies.
I found this one working. And remove autoincrement from id
String strSQL1 = "UPDATE people_table SET id = (id +1) WHERE id < 0";
String strSQL = "UPDATE people_table SET id = (id -1) WHERE id > 1";
db.execSQL(strSQL);
db.execSQL(strSQL1);

GROUP BY / HAVING CLAUSE Failing in Sqlite

Inside a list, I have an item : shop.
Shop have the same id, but different group_id.
The primary key is shop_id, group_id
The first time I display the list, I want to group those 2 ( As it is the same shop)
In the next screen.
So, my query is :
SELECT * FROM TABLE_SHOP GROUP BY ID
But then, When I process it, I need to separate the shops with field "done" = 0/1
if done = 1, I will disable the element.
I tried to add a clause :
SELECT * FROM TABLE_SHOP GROUP BY ID HAVING DONE =0
But then, when one is done, the shop doesn't appear anymore in the list. ( instead of showing one disabled, and one enabled)
Any idea of what is failing in the list?
EDIT :
I changed fields name to match picture.
In this case, I want to display the list.
The first time I display it ( all done fields in 0), I want the shop 25975 ( 2 records ) appears just once ( that's why I used GROUP BY)
Then, when I process a shop, it will update his done field to 1.
So in this moment, I don't wan't to group anymore the 2 rows. It it clearer???
The purpose of HAVING clause is to filter using aggregate functions.
Try this instead:
SELECT * FROM TABLE_TIENDA WHERE TIENDA_DONE = 0 GROUP BY TIENDA_ID
I got it working.
The query was :
SELECT * FROM TABLE_TIENDA WHERE TIENDA_DONE = 0 GROUP BY TIENDA_ID, DONE
Tx for your help

How to limit the total number of records for a given table?

I'm using sqlite3 as a caching tool for an android app.
Basically, a services fetches data from a server at a regular interval and inserts the new records inside a sqlite3 table. The data is then used to populate UI inside activities and fragments.
Because the data is short-lived, it does not need to be persisted long-term.
In order to save space and resources, how can I make sure that say, only the 100 most recent records are kept and older entries are automatically deleted ?
I've heard of TRIGGERS but not too sure about how to implement them. Any pointers would be appreciated.
Follow the steps
1) Add one column in your table "timestamp"
2) During insert the record set the "timestamp" with current time in milliseconds.
3) Create Trigger like this
CREATE TRIGGER yourtriggername AFTER INSERT
ON yourtable WHEN (SELECT COUNT(*) FROM yourtable) >100
BEGIN
DELETE FROM yourtable WHERE timestamp = (SELECT MIN(timestamp) FROM yourtable)
END
4) Replace "yourtable" with actual table name
5) The above trigger will called every time and check whether the total records in table exceeds 100 it will remove the record whose "timestamp" is minimum.
select entry_id
from entries
order by create_date desc
limit 1 offset 100;
delete from entries where create_date <
(select create_date from entries where entry_id = obtained_entry_id);
Or just:
delete from entries where create_date <
(select create_date from entries by create_date desc limit 1 offset 100);
Trigger to enforce it:
CREATE TRIGGER truncate_entries AFTER INSERT ON entries
BEGIN
--the delete statement from above
END;

SQLite, merging two tables based on higher value in a column

I've got two SQLite databases, each with a table that I need to keep synchronized by merging rows that have the same key. The tables are laid out like this:
CREATE TABLE titles ( name TEXT PRIMARY KEY,
chapter TEXT ,
page INTEGER DEFAULT 1 ,
updated INTEGER DEFAULT 0 );
I want to be able to run the same commands on each of the two tables, with the result that for pairs of rows with the same name, whichever row has the greater value in updated will overwrite the other row completely, and rows which do not have a match are copied across, so both tables are identical when finished.
This is for an Android app, so I could feasibly do the comparisons in Java, but I'd prefer an SQLite solution if possible. I'm not very experienced with SQL, so the more explanation you can give, the more it'll help.
EDIT
To clarify: I need something I can execute at an arbitrary time, to be invoked by other code. One of the two databases is not always present, and may not be completely intact when operations on the other occur, so I don't think a trigger will work.
Assuming that you have attached the other database to your main database:
ATTACH '/some/where/.../the/other/db-file' AS other;
you can first delete all records that are to be overwritten because their updated field is smaller than the corresponding updated field in the other table:
DELETE FROM main.titles
WHERE updated < (SELECT updated
FROM other.titles
WHERE other.titles.name = main.titles.name);
and then copy all newer and missing records:
INSERT INTO main.titles
SELECT * FROM other.titles
WHERE name NOT IN (SELECT name
FROM main.titles);
To update in the other direction, exchange the main/other database names.
For this, you can use a trigger.
i.e.
CREATE TRIGGER sync_trigger
AFTER INSERT OR UPDATE OF updated ON titles
REFERENCING NEW AS n
FOR EACH ROW
DECLARE updated_match;
DECLARE prime_name;
DECLARE max_updated;
BEGIN
SET prime_name = n.name;
ATTACH database2name AS db2;
SELECT updated
INTO updated_match
FROM db2.titles t
WHERE t.name=prime_name)
IF updated_match is not null THEN
IF n.updated > updated_match THEN
SET max_updated=n.updated;
ELSE
SET max_updated=updated_match;
END IF;
UPDATE titles
SET updated=max_updated
WHERE name=prime_name;
UPDATE db2.titles
SET updated=max_updated
WHERE name=prime_name;
END IF;
END sync_trigger;
The syntax may be a little off. I don't use triggers all that often and this is a fairly complex one, but it should give you an idea of where to start at least. You will need to assign this to one database, exchanging "database2name" for the other database's name and then assign it again to the other database, swapping the "database2name" out for the other database.
Hope this helps.

Categories

Resources