Limiting number of rows in SQLite - android

I have a situation where I just want to limit only 50 rows in a table. If user inserts a new row after that then first row (which was inserted very first) should get deleted and new row gets inserted, so that count remains same.
I know that I can have an rowid field and while inserting new record I can check if there are already 50 rows so delete the smallest rowid and then insert the new one. But just wanna know if there is any better solution so that I don't have to do 3 database operations (1. query #of rows, 2. delete minimum, 3. insert)

I know a way that works, but it's a little ugly. It relies on carefully constructed constraints and on seeding the database. For brevity, I'm using just five rows instead of 50.
create table test (
row_num integer primary key
check ((round(row_num) = row_num) and (row_num between 1 and 5)),
other_columns char(1) not null default 'x',
row_timestamp timestamp
not null unique
default current_timestamp
);
The expression round(row_num = row_num) guarantees you have integers in the row_num column. Otherwise, SQLite would let you insert 1.54 or 'wibble' in there.
The other_columns column is just a placeholder for your actual data.
insert into test (row_num, row_timestamp) values
(1, '2015-01-01 08:00:01'),
(2, '2015-01-01 08:00:02'),
(3, '2015-01-01 08:00:03'),
(4, '2015-01-01 08:00:04'),
(5, '2015-01-01 08:00:05');
The actual timestamp values don't really mean anything. Not yet, anyway. Seeding the database like this means that, from now on, you only have to execute update statements. If the table were empty to start with, you'd have to deal with different logic for inserts and updates. For example, you'd have to count rows to figure out whether to insert or to update.
create trigger update_timestamp
after update on test
for each row
begin
update test
set row_timestamp = strftime('%Y-%m-%d %H:%M:%f', 'now')
where row_num = OLD.row_num;
end;
The "update_timestamp" trigger makes SQLite maintain the timestamp with fractions of a second (%f). Might depend on whether the underlying OS supports fractional precision.
create trigger no_deletes
after delete on test
for each row
begin
-- There might be a more elegant way to prevent deletes.
-- This way just inserts exactly what a delete statement deletes.
insert into test (row_num, other_columns, row_timestamp)
values (OLD.row_num, OLD.other_columns, OLD.row_timestamp);
end;
Now you can update data. You update your own data, which here is just the placeholder other_columns, and SQLite takes care of the rest.
update test
set other_columns = 'b'
where row_timestamp = (select min(row_timestamp) from test);
select * from test order by row_timestamp desc;
row_num other_columns row_timestamp
---------- ------------- -----------------------
1 b 2015-03-08 12:43:21.926
5 x 2015-01-01 08:00:05
4 x 2015-01-01 08:00:04
3 x 2015-01-01 08:00:03
2 x 2015-01-01 08:00:02

Related

How can I get SUM values for each rows on SELECT without go twice in the table and without use 'WITH' clause?

I need get the total SUM for each rows in my query, but I don't want go twice in the table.
I tried do this:
SELECT id, value, SUM(value) as total FROM product
But my result was this:
id value total
3 30 60
If I do the bellow query I get my wanted result, but I need go twice in the table:
SELECT id, value, (SELECT SUM(value) FROM product) as total FROM product
Or if I use 'WITH' clause, but this is not supported before Android 5:
WITH data AS (SELECT id, value FROM product)
SELECT id, value, (SELECT SUM(value) FROM data) as total FROM data
Wanted result:
id value total
1 10 60
2 20 60
3 30 60
Thank you!
It's not possible using your SQLite version. You'll have to use two selects.
Basically you have to use a subquery.
However, perhaps you may be less concerned about the 2nd table as I believe that the Query Planner will determine that it only needs to calculate the sum once and does away with the need for a variable as it stores the value in cache.
I believe that the results of using EXPLAIN QUERY PLAN your_query shows this. i.e. using
EXPLAIN QUERY PLAN SELECT id, value, (SELECT sum(value) FROM products) AS total FROM products;
results in :-
This being explained as (see bolded statements) :-
1.3. Subqueries
In all the examples above, the first column (column "selectid") is
always set to 0. If a query contains sub-selects, either as part of
the FROM clause or as part of SQL expressions, then the output of
EXPLAIN QUERY PLAN also includes a report for each sub-select. Each
sub-select is assigned a distinct, non-zero "selectid" value. The
top-level SELECT statement is always assigned the selectid value 0.
For example:
sqlite> EXPLAIN QUERY PLAN SELECT (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2;
0|0|0|SCAN TABLE t2
0|0|0|EXECUTE SCALAR SUBQUERY 1
1|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)
0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2
2|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?)
The example above contains a pair of scalar subqueries assigned
selectid values 1 and 2. As well as a SCAN record, there are also 2
"EXECUTE" records associated with the top level subquery (selectid 0),
indicating that subqueries 1 and 2 are executed by the top level query
in a scalar context. The CORRELATED qualifier present in the EXECUTE
record associated with scalar subquery 2 indicates that the query must
be run separately for each row visited by the top level query. Its
absence in the record associated with subquery 1 means that the
subquery is only run once and the result cached. In other words,
subquery 2 may be more performance critical, as it may be run many
times whereas subquery 1 is only ever run once.
Unless the flattening optimization is applied, if a subquery appears
in the FROM clause of a SELECT statement, SQLite executes the subquery
and stores the results in a temporary table. It then uses the contents
of the temporary table in place of the subquery to execute the parent
query. This is shown in the output of EXPLAIN QUERY PLAN by
substituting a "SCAN SUBQUERY" record for the "SCAN TABLE" record that
normally appears for each element in the FROM clause. For example:
sqlite> EXPLAIN QUERY PLAN SELECT count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x;
1|0|0|SCAN TABLE t1 USING COVERING INDEX i2
0|0|0|SCAN SUBQUERY 1
0|0|0|USE TEMP B-TREE FOR GROUP BY
If the flattening optimization is used on a subquery in the FROM
clause of a SELECT statement, then the output of EXPLAIN QUERY PLAN
reflects this. For example, in the following there is no "SCAN
SUBQUERY" record even though there is a subquery in the FROM clause of
the top level SELECT. Instead, since the flattening optimization does
apply in this case, the EXPLAIN QUERY PLAN report shows that the top
level query is implemented using a nested loop join of tables t1 and
t2.
sqlite> EXPLAIN QUERY PLAN SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1;
0|0|0|SEARCH TABLE t2 USING INDEX i4 (c=?)
0|1|1|SCAN TABLE t1
EXPLAIN QUERY PLAN
End Note
Perhaps of relevance is this statement :-
The best feature of SQL (in all its implementations, not just SQLite)
is that it is a declarative language, not a procedural language. When
programming in SQL you tell the system what you want to compute, not
how to compute it. The task of figuring out the how is delegated to
the query planner subsystem within the SQL database engine.
Query Planning
You may also find this of interest he SQLite Query Optimizer Overview noting that as of release 3.8.0 The Next-Generation Query Planner is utilised.

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;

How do I keep my SQLite Database organized? Android

Say my SQLite Databate has 2 columns, the first being an auto-incrementing ID and the 2nd being some string. Say right now it's
1 random
2 jellybean
3 ImTired
if I were to delete entry 2, it would then be
1 random
3 ImTired
What I want is a way to make it so when you delete entry 2, it turns it into
1 random
2 ImTired
I thought about updating the entries to shift them all down one and delete the last one, but even if it worked(in my case, it deleted all of my entries, but whatever...), and even if I did get it to
1 random
2 ImTired
the next time I create a new entry, it'll be entry 4. I don't think this necessary to my app, but it seriously bugs me.
The ID column on your DB is working as a Primary Key, which is a column or group of columns used to uniquely identify a row. Once you set a Primary Key on a row you shouldn't change it, else you risk losing the consistency of the DB. For instance, suppose you later create another table that references the rows in your first table. That reference will be made using the Primary Key, and if you later change it your data won't make sense anymore.
If you wanted the ID column to keep changing just to reflect the number of rows in your table you can solve that problem with other methods. For instance. SQL offers a COUNT operator that will return the number of rows in your table:
SELECT COUNT(*) FROM Table_name;

Android - easy/efficient way to maintain a "cumulative sum" for a SQLite column

What is the best way to maintain a "cumulative sum" of a particular data column in SQLite? I have found several examples online, but I am not 100% certain how I might integrate these approaches into my ContentProvider.
In previous applications, I have tried to maintain cumulative data myself, updating the data each time I insert new data into the table. For example, in the sample code below, every time I would add a new record with a value score, I would then manually update the value of cumulative_score based on its value in the previous row.
_id score cumulative_score
1 100 100
2 50 150
3 25 175
4 25 200
5 10 210
However, this is far from ideal and becomes very messy when handling tables with many columns. Is there a way to somehow automate the process of updating cumulative data each time I insert/update records in my table? How might I integrate this into my ContentProvider implementation?
I know there must be a way to do this... I just don't know how. Thanks!
Probably the easiest way is with a SQLite trigger. That is the closest I know
of to "automation". Just have an insert trigger that takes the previous
cumulative sum, adds the current score and stores it in the new row's cumulative
sum. Something like this (assuming _id is the column you are ordering on):
CREATE TRIGGER calc_cumulative_score AFTER INSERT ON tablename FOR EACH ROW
BEGIN
UPDATE tablename SET cumulative_score =
(SELECT cumulative_score
FROM tablename
WHERE _id = (SELECT MAX(_id) FROM tablename))
+ new.score
WHERE _id = new._id;
END
Making sure that the trigger and the original insert are in the same
transaction. For arbitrary updates of the score column, you would have to
have to implement a recursive trigger that somehow finds the next highest id (maybe by selecting by the min id
in the set of rows with an id greater than the current one) and updates its
cumulative sum.
If you are opposed to using triggers, you can do more or less the same thing in
the ContentProvider in the insert and update methods manually, though since
you're pretty much locked into SQLite on Android, I don't see much reason not to
use triggers.
I assume you are wanting to do this as an optimization, as otherwise you could just calculate the sum on demand (O(n) vs O(1), so you'd have to consider how big n might get, and how often you need the sums).

SQLite UPDATE value with row number possible/options?

Hi i'm working on an SQLite viewer in android using java and shell commands (so commands have to be one line) i've written the layout and viewer and all everything is perfect. I set it up so when a value is long pressed an edit text shows where the user can input the new value and then when okay is pressed it should update the value.
I have the column name, old and new value, database name, table name etc however the issue is allowing them to update the value i've seen things like the where clause but the issue is if a column has the value multiple times (which could very well be the case) it won't know the correct row.
So bear in mind i'm new to sqlite been working with it less than a week. Is there a way i can update the column value with the columns name and row number?
What are my options to update the table.
I also don't really under stand this say i have a table like this (one column)
sample
--------
0
0
0
1
0
1
1
0
How would you update the third row to equal 1? If I did
sqlite3 DATABASEHERE "UPDATE TABLEHERE SET sample='1' WHERE sample='0'"
The where statement describes rows 1, 2, 3, 5, and 8 so there has to be a way to use row number?
Thanks for any help
SQLite gives you access to rowid. So you can write SELECT rowid, col FROM table1 and then use it to update the table :UPDATE TABLEHERE SET sample='1' WHERE rowid=3

Categories

Resources