Alternative for Window function in SQLite version < 3.25.0? - android

I want retrieve the last 10 rows for each chat_id match in a table.
This works perfect:
SELECT *
FROM
(SELECT a.*,
row_number()
OVER (PARTITION BY a.chat_id
ORDER BY a.timestamp DESC ) AS row
FROM messages a ) AS foo
WHERE row <= 10
But when i put this code on my React Native app, it throws and error, and its because this its only supported in SQLite 3.25.0 and above.
https://developer.android.com/reference/android/database/sqlite/package-summary.html
Is there another way to do this? I'm trying to avoid multiple queries to SQL.

One option is to use a correlated subquery to count how many records have the same chat_id and a greater timestamp than the current row, and use the result for filtering:
select m.*
from messages m
where (
select count(*)
from messages m1
where m1.chat_id = m.chat_id and m1.timestamp > m.timestamp
) < 10

Related

Sum() function with over not work in Room database android

Can anyone help me the below query is working fine in DB Browser but not work with the Room database. Here are the table and query:
Table Transaction:
id
amount
is_credit
Query:
SELECT *
FROM
(SELECT
id, amount,
SUM(CASE WHEN is_credit = 1 THEN amount ELSE -amount END) OVER (ORDER BY id) AS balance
FROM `Transaction`)
ORDER BY
id DESC;
I have tried this query with SimpleSQLiteQuery but I'm getting error :
E/SQLiteLog: (1) near "(": syntax error
If your version of SQLite is lower than 3.25.0 then you can't use window functions like SUM().
Instead you can do it with a correlated subquery:
SELECT t.id,
t.amount,
(SELECT SUM(CASE WHEN tt.is_credit = 1 THEN 1 ELSE -1 END * tt.amount) FROM `Transaction` tt WHERE tt.id <= t.id) AS balance
FROM `Transaction` t
ORDER BY t.id DESC;
Or a self join:
SELECT t.id,
t.amount,
SUM(CASE WHEN tt.is_credit = 1 THEN 1 ELSE -1 END * tt.amount) AS balance
FROM `Transaction` t INNER JOIN `Transaction` tt
ON tt.id <= t.id
GROUP BY t.id
ORDER BY t.id DESC;
See a simplified demo.
You are trying to use SQLite Windows functions i.e OVER the version of SQLite on Android Devices is typically some way behind. You'd need at least API 30 (Android R) for SQLite version 3.28.0 (when Windows functions were introduced). Obviously that would place limitations on the App's install base.
You may wish to see Version of SQLite used in Android?

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;

SQLite: Return the most recent 'n' rows per unique value of column 'y'

I have a table with the following schema:
CREATE TABLE table (
msg_id TEXT,
thread_id TEXT,
.
.
.
date INTEGER
)
I need to retrieve the most recent n msg_id per unique value of thread_id. Is there a way to do it using a single query or will I need to query the database to get the most recent distinct thread_ids, then query the database again PER unique thread_id? I recall reading somewhere that multiple database queries can get expensive.
You could use a correlated subquery. For example, for N = 5 :
select *
from YourTable yt1
where 5 <
(
select count(*)
from YourTable yt2
where yt2.thread_id = yt1.thread_id
and yt2.msg_id < yt1.thread_id
)
This is not too fast, so you might be better of with multiple queries.

Combining 2 SELECT expression with LIMIT in both

I'm developing an Android application. I have a database table and I want to request two or more different SELECT expression with different WHERE condition and the same LIMIT for each expression. My query is like:
(SELECT * FROM questions WHERE level=1 LIMIT 5)
UNION
(SELECT * FROM questions WHERE level=2 LIMIT 5)
When running application this query causes error that says:
near "UNION": syntax error: while compiling <here is my query>
When I omit the LIMIT it works well but LIMIT and ORDER BY don't work with UNION this way. My query is correct for MySQL according their documentation. But I couldn't find any SQLite documentation for my problem. So how should I query SQLite table for my needs?
Try
SELECT * FROM
(
SELECT * FROM
(SELECT * FROM questions WHERE level=1 LIMIT 5)
UNION
SELECT * FROM
(SELECT * FROM questions WHERE level=2 LIMIT 5)
)

Categories

Resources