WITH RECURSIVE query with android room - android

Need to get rows with last 10 recent dates from table, even if some of the dates are missing from table.
This query is working fine to get dates but the problem is same query in android room dao throwing compile error at 'SELECT x+1' x cannot be resolved.
#Query(WITH RECURSIVE
stepDtailsTable(x) AS (
SELECT 0
UNION ALL
SELECT x + 1 FROM stepDtailsTable
LIMIT(SELECT((julianday('2019-03-20')-julianday('2019-03-01')))+1)
)
SELECT date(julianday('2019-03-01'),'+'||x||' days')as date FROM stepDtailsTable")
StepDetailsPojo getLastTenDayData();

I have noticed that you are using constants for the julian dates whereas you wanted to query the last 10 recent dates from a table. Here is a query that does that, with filling gaps from the maximum date found in the table to 10 days earlier. I cannot test on your platform so it may not resolve the compiler error you are getting, maybe because RECURSIVE is not supported. Hopefully it gets you a step further.
WITH RECURSIVE
cte(x) AS (
SELECT julianday(date(max(thedate))) from TheDataTable
UNION ALL
SELECT x-1 FROM cte
LIMIT 10
)
SELECT date(x) FROM cte

Related

SQLite Group By results order behaviour change 3.28.0 onwards

I have noticed an issue with the order of results using "GROUP BY" with later versions of SQLite that am struggling to solve. I first noticed it when Android 11 came out as it uses SQLite 3.28.0. I have now pinned it down to a behaviour change in SQLite itself.
The issue is the the order of results is changed depending on the version. I'm not sure if it's a bug or intended behaviour or an error on my part (albeit an error that has been working fine for 8 years with older SQLite versions).
Example table:
id|year|command |code
1 |2005|TV |A
2 |2005|TV-CD |B
3 |2005|CD |B
4 |2010|TV |B
5 |2015|TV |C
If I run the following command
SELECT * FROM myTable GROUP BY command ORDER BY _id
With in SQLite 3.22 (Android 10) or 3.23.1 I get:
2|2005|TV-CD|B
3|2005|CD |B
5|2015|TV |C
Which is what I want...
If I run the same command in SQLite 3.28 (Android 11) or higher I get
1|2005|TV |A
2|2005|TV-CD|B
3|2005|CD |B
Here's a quick sample of the table if you want to try it for yourself
CREATE TABLE 'myTable' ('_id' integer PRIMARY KEY ,'year' NUMERIC ,'command' TEXT, 'code' TEXT);
INSERT INTO myTable VALUES ("1","2005","TV","A");
INSERT INTO myTable VALUES ("2","2005","TV-CD","B");
INSERT INTO myTable VALUES ("3","2005","CD","B");
INSERT INTO myTable VALUES ("4","2010","TV","B");
INSERT INTO myTable VALUES ("5","2015","TV","C");
SELECT * FROM myTable GROUP BY command ORDER BY _id
https://www.jdoodle.com/execute-sql-online/ was useful for testing as it allows you to change the SQLite version on the fly.
As I already mentioned in my comment, the result of a query like:
SELECT * FROM myTable GROUP BY command
is unpredictable, because GROUP BY should be used only for aggregation, something like:
SELECT command, MAX(year) AS max_year
FROM myTable
GROUP BY command
which will return the max year for each command.
So if the behavior of GROUP BY with SELECT * ... has changed in newer versionsshould not be a problem if you did not use such queries.
From what I read in your question, you are expecting in the results for each command the row with the max id.
In standard sql and without window functions, which are still not supported in most versions of android SQLite, you could aggregate first and then join to the table.
But, SQLite has a documented feature to use so that you can use bare columns. This is valid:
SELECT MAX(_id) AS _id, year, command, code
FROM myTable
GROUP BY command
ORDER BY _id
You will get the row with the max _id for each command.
I've found a solution and can confirm it works in both versions of SQLite
SELECT _id,MAX (year),command,code FROM myTable GROUP BY command ORDER BY _id

Alternative for Window function in SQLite version < 3.25.0?

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

SQL query does not work on earlier Android versions

I have the following SQL query in my app. It works without any issues on my Nexus 7, but crashes with a message about the column "title" not existing on earlier versions of Android.
SELECT feeds._id AS _id, feeds.title, feeds.description, feeds.image,
feeds.groupName, count(items._id) AS itemCount, sum(case when items.read = 0
then 1 end) as unreadCount FROM feeds LEFT JOIN items ON (items.feedId = feeds._id)
GROUP BY feeds._id ORDER BY feeds.groupName
Is this a known issue, or is there something wrong with my query?
Here is a complete sqlite tutorial. Be carefull about your database version. Change it first for every table structure change.

SQLITE query monthly GROUP BY with SUM

I have been stuck with this problem for a while (Android SQLITE). I have 2 columns - Date and Amount. Date is stored in YYYYMMDD format.
DATE AMOUNT
20120521-------50
20120506-------40
20120311-------30
20120202-------20
20120125-------10
What I need is a SQL query (Android SQLITE), which will output two columns - Month and cumulative total till that month..If a month does not have any transaction, it still should evaluate the cummulative total.
So the output I need here (notice there are no transactions for April)
Month Cumulative-Total
MAY-------150
APR-------60
MAR-------60
FEB-------30
JAN-------10
It should work:
SELECT strftime('%m', date), SUM(Amount)
FROM myTable
GROUP BY strftime('%m', date)
Sorry i forgot this part of your question "..If a month does not have any transaction, it still should evaluate the cummulative total."
Solution: The simple solution is to have a DUMMY entry in the table for all months.
Difficult since the easy way to do this is to write a stored procedure to do it but which isnt supported in sqlite. However take a look at this - maybe this way might be useful
Android - easy/efficient way to maintain a "cumulative sum" for a SQLite column
Also you can try doing the cumulative addition on the android end
you can not just comulate like this. You can do this 2 ways:
Group by month and then sum it up on the Android end and add the Months that missing.
Try make that with a store procedure. (this I am not entierly sure possable on sql lite for your case.) Read this
So I would go with the first option.

How do you count the number of consecutive dates in a table?

In my Android App I need to keep track of the longest streak, and current streak, of consecutive dates that are saved in a database table. I don't even know where to start to get this to work. The best I can come up with is to query every row in the table and iterate through all of them programmatically to find where there's a gap. Not very efficient. Anyone have any better ideas?
Here is an SQL only solution that I thought was really cool. Assuming the dates in your table are unique (not that it would be too hard to just group on them) you can use the method adapted from here http://www.sqlteam.com/article/detecting-runs-or-streaks-in-your-data. I ran through the example and there are some syntax errors, so hopefully I didn't repeat them in my answer below. I probably used some reserved keywords, so you may need to adapt that.
First create a table of Dates that is significantly large to cover your needs. I'm not sure what the best method for SQLite is, but in SQL Server you can insert integers into a Datetime field and it will do an implicit conversion of integers to dates. There are many methods to insert integers into tables...
Anyway, once the Dates table is created, do a left join from your Dates table to your Streak table using your min and max dates from your streak table as your range limiter. You will then have the following code.
Let’s call it SQL 0
SELECT Dates.Date,
CASE
WHEN StreakTable.DATE IS NULL THEN 0
ELSE 1
END AS Result
FROM Dates
LEFT JOIN StreakTable
ON Dates.DATE = StreakTable.DATE
WHERE Dates.DATE BETWEEN (SELECT MIN(DATE) AS MinDate
FROM StreakTable) AND (SELECT MAX(DATE) AS MaxDate
FROM StreakTable)
Let’s call the following SQL 1
SELECT Date,
Result,
(SELECT COUNT(*)
FROM (SQL 0) S
WHERE S.Result <> SV.Result
AND S.GameDate <= SV.GameDate) AS RunGroup
FROM (SQL 0) SV
Let’s call the following SQL 2
SELECT Result,
MIN(Date) AS StartDate,
MAX(Date) AS EndDate,
COUNT(*) AS Days
FROM (SQL 1) A
GROUP BY Result,
RunGroup
ORDER BY MIN(Date)
At this point you can do some pretty cool stuff like answer:
What was the longest streak?
SELECT TOP 1 *
FROM (SQL 2) A
WHERE Result = 1
ORDER BY Games DESC
What is the current streak as of the most recent date?
SELECT *
FROM (SQL2) A
WHERE EndDate = (SELECT Max(Date)
FROM Streak)
How many streaks of 3 or more did we have?
SELECT Result,
COUNT(*) as NumberOfStreaks
FROM (SQL 2) A
GROUP BY Result
WHERE Days >= 3
Basically you have a month and days in a month
so you just compare the days count to the needed number.
If there's a gap you can easily find it out by substracting the count from days in a month. E.g. you have count(days_visited) where month=1
and it returns you 20 days but January has 31 so there's a gap in 11 days and here're the date functions of sqlite
http://www.sqlite.org/lang_datefunc.html
You can use following functions
like SELECT date('now','start of year','+9 months','weekday 2');
EDIT
sorry everyone solution is ugly. it is I know.
create table visits(day_visited timestamp,visited int);
You create a record everyday in this table indicating
whether a user was online or offline with
'now',1 or 0 (online/offline). Then you run through there records.
Your records for month will be an int array with 1s and 0s.
called vistedrecordsformonth
pseudo code:
int online=0;
int offline=0;
for(int i=0;i<vistedrecordsformonth.size();i++){
boolean flag=false;
if(vistedrecordsformonth[i]==1){ //visited
if(!flag&&i!=0) //check for flag and not 0 index to insert a record
{
streaksMap.put(online,offline); //or insert a record into another streakmap or table
online=0;
offline=0;
}
flag=true;
online++;
}
else{
flag=false;
offline++;
}
} //end of for
The map or table will contain a pair of online=offline days for a month.
with usual order by you can see what was the biggest streak in online or offline days.
It is ugly I know I'm sure there should be something more elegant but as
quick and dirty it should work.
hope it helps abit.

Categories

Resources