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
Related
I am trying to do this query in android FTS4 table and this works perfectly:
SELECT * from table WHERE table MATCH 'description: paint* OR alias: paint*'
I need to match multiple words in multiple columns like this:
SELECT * from table WHERE table MATCH 'description: seal* AND paint* OR alias: seal* OR paint*'
This doesn't work in android but works in any DB browser.
I have tried many combinations such as below, they all work in the browser but not in android.
SELECT * from table WHERE table MATCH '(description: seal* AND paint*) OR (alias: seal* OR paint*)'
SELECT * from table WHERE table MATCH 'description: (seal* AND paint*) OR alias: (seal* OR paint*)'
The documentation of sqlite3 doesn't specify any solution for multiple columns with multiple words.
Also in this question the query works in the current android environment as mentioned above in my first line of code. Maybe it didn't work in the past but now it works. My problem is regarding multiple values with OR/AND as described not multiple columns.
Is there any way to achieve this thing in Android?
I have ids in my table, ids start from 1 to 20, I want a query, to find the first and last records in a given table but I want the result by some condition.
For example: if I have the record
1,2,3,4,5,9,10,11,12,13, 19,20
I need a result like 1-5, 9-13, 19-20 like this I need results
This is the island part of the classic gaps and islands problem (With the gaps part being finding the missing values in between each island). If you search for that term, you'll find a ton of material about how to calculate them.
One approach (Requires Sqlite 3.25 or newer for window function support):
sqlite> CREATE TABLE ex(id INTEGER PRIMARY KEY);
sqlite> INSERT INTO ex VALUES (1),(2),(3),(4),(5),(9),(10),(11),(12),(13),(19),(20);
sqlite> WITH cte AS (SELECT id, id - row_number() OVER (ORDER BY id) AS grp FROM ex)
...> SELECT min(id) AS rangestart, max(id) AS rangeend FROM cte GROUP BY grp;
rangestart rangeend
---------- ----------
1 5
9 13
19 20
SQL Query to find first record in your table:
SELECT * FROM <table_name> ORDER BY <column_name> ASC LIMIT 1
SQL Query to find last record in your table:
SELECT * FROM <table_name> ORDER BY <column_name> DESC LIMIT 1
For example: if I have the record 1,2,3,4,5,9,10,11,12,13, 19,20
I need a result like 1-5, 9-13, 19-20 like this I need results
If you need result like you have mentioned, then you can set LIMIT in your query to get how many records you can have in that query.
QUERY:
SELECT * FROM <table_name> LIMIT <any_number>
I am trying to create a static list of timestamps so that i can join them agains another table to create chart data. So far I have a query in this format SELECT * FROM (VALUES('a'),('b'),('c'),('d')) AS tbl ,which is working in sqlitestudio but not in android 4.4. When I run the query in the phone I get the error
android.database.sqlite.SQLiteException: near "VALUES": syntax error (code 1): , while compiling: SELECT * ...
I have also tried wrapping the values term inside another select like this SELECT * FROM (SELECT * FROM (VALUES('a'),('b'),('c'),('d'))) AS tbl but I still get the same error.
The full query now looks like this
SELECT * FROM (select * from ( VALUES (1458111312025),
(1455667200000),
(1455753600000),
(1455840000000),
(1455926400000),
(1456012800000),
(1456099200000),
(1456185600000),
(1456272000000),
(1456358400000),
(1456444800000),
(1456531200000),
(1456617600000),
(1456704000000),
(1456790400000),
(1456876800000),
(1456963200000),
(1457049600000),
(1457136000000),
(1457222400000),
(1457308800000),
(1457395200000),
(1457481600000),
(1457568000000),
(1457654400000),
(1457740800000),
(1457827200000),
(1457913600000),
(1458000000000),
(1458086400000))) i LEFT JOIN (
SELECT (osysdate- (osysdate % 86400000) ) interval, SUM(field004) totalval FROM onlineactivities WHERE field003 !=300000 and osysdate> 1455605711999 GROUP BY interval )
onl ON i.'' = onl.interval;;
Note that this works in sqlitestudio with sqlite version 3.8.10 but not in android kitkat (not sure about the sqlite version in it) What could be the problem?
Also please check out this question which is what I am trying to do but with sqlite and this answer
The VALUES syntax is available since SQLite 3.8.3, which is not available in every Android version.
To be compatible with earlier SQLite versions, you have to use a compound query:
SELECT 1458111312025 UNION ALL
SELECT 1455667200000 UNION ALL
...
Alternatively, put all the values into a temporary table.
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.
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.