How can I limit the number of rows in Android SQLite table - android

I create an Android app that has "recents history".
I would like to limit the size of this table to a maximum of 50 rows (according to their insert date).
I saw several threads that talk about limiting the number of deleted rows, but I'm not sure if this feature is even enabled in Android's SQLite.
Can anyone help here?
Thanks

Create a trigger
CREATE TRIGGER delete_till_50 INSERT ON _table WHEN (select count(*) from _table)>50
BEGIN
DELETE FROM _table WHERE _table._id IN (SELECT _table._id FROM _table ORDER BY _table._id limit (select count(*) -50 from _table ));
END;
EDIT:
You can change
DELETE FROM ... WHERE ... IN ...
to
DELETE FROM ... WHERE ... NOT IN ...
as Mojo Risin wrote. I'm not sure about difference of performance for large tables for using IN and NOT IN, but for your problem it's no difference.

I think sql can't manage the number of rows in your table so you'll have to manage it by yourself. You can execute query after data insertion that will reduce the data - something like this should work
DELETE FROM table where _id NOT IN (SELECT _id from table ORDER BY insertion_date DESC LIMIT 50)

Check out SearchRecentSuggestions's source code for an example. It has a method to truncate history up to a given number of entries, using LIMIT -1 OFFSET <maxEntries>. You have to sort the entries by the reversed order of insertion first, then skip the first maxEntries.
If you call this automatically every upon insertion, then you only need to LIMIT 1 as there can never be more than maxEntries + 1 anyway.
/**
* Reduces the length of the history table, to prevent it from growing too large.
*
* #param cr Convenience copy of the content resolver.
* #param maxEntries Max entries to leave in the table. 0 means remove all entries.
*/
protected void truncateHistory(ContentResolver cr, int maxEntries) {
if (maxEntries < 0) {
throw new IllegalArgumentException();
}
try {
// null means "delete all". otherwise "delete but leave n newest"
String selection = null;
if (maxEntries > 0) {
selection = "_id IN " +
"(SELECT _id FROM suggestions" +
" ORDER BY " + SuggestionColumns.DATE + " DESC" +
" LIMIT -1 OFFSET " + String.valueOf(maxEntries) + ")";
}
cr.delete(mSuggestionsUri, selection, null);
} catch (RuntimeException e) {
Log.e(LOG_TAG, "truncateHistory", e);
}
}

Related

I'm trying to get the first Min value from a SQL database in android

Sorry, I'm new. I have a table and need to get the column ID of the first min value of the table. The table is organized so the values keep decreasing until they get to 0 and all subsequent values are equal to zero.
It is possible for none of the values to be zero in which case Id need the last ID. It is important that I only have one return ID because of how I'm implementing it. This is the code I tried first but I'm getting an error.
I did not try to add the exception of there being no 0s here because I thought it might be easier to add an If statement in the implementation of the method I use.
The error I get confuses me because It seems like I can't use FIRST when I thought I could, but here it is:
android.database.sqlite.SQLiteException: no such function: FIRST (code
1): , while compiling: SELECT FIRST (_id) FROM graph WHERE bac = 0;
My code:
public int getWhereZero(){
int zero = 0;
SQLiteDatabase db = getReadableDatabase();
String query = "SELECT FIRST (" + COLUMN_ID
+ ") FROM " + TABLE_GRAPH
+" WHERE " + COLUMN_BAC
+ " = 0;";
Cursor cursor = db.rawQuery(query, null);
if(cursor != null){
cursor.moveToFirst();
zero = cursor.getInt(cursor.getColumnIndex(COLUMN_ID));
cursor.close();
}
return zero;
}
SQLite doesn't have a FIRST() function. However, you can limit the number of rows returned to one using LIMIT, so sorting by the desired order will get the row you need:
SELECT column_id FROM graph ORDER BY bac LIMIT 1;

Content loader reverse cursor order

I have a simple chat application. I am using content loaders to display messages inside a channel. Since I want 100 recent messages I sort messages based on the time stamp desc and limit it to 100. but I want to display recent messages at the bottom of the screen. Right now the cursor adaptor displays recent message first. How do I reverse the order of the cursor ?
According to me, I will have to write some smart query inside content provider or somehow have to manipulate the cursor inside cursor adaptor's bindView.
Try to sort your messages based on timestamp ASC instead of DESC.
edit: Sorry, didn't grasp the subtility here.
You can try something like this to get data in reverse order:
for (cursor.moveToLast(); !cursor.isBeforeFirst(); cursor.moveToPrevious())
{
// do your magic
}
I would probably use subquery
SELECT * FROM table ORDER BY timestamp WHERE _id
IN (SELECT _id FROM table ORDER BY timestamp DESC LIMIT 100)
To use that with content provider:
String sel = BaseColumns._ID + " IN (SELECT " + BaseColumns._ID +
" FROM " + MyDb.TableName._TABLE_NAME + " ORDER BY " +
MyDb.TableName.TIMESTAMP + " DESC LIMIT 100)";
Cursor q = getContentResolver().query(MyDb.TableName.CONTENT_URI,
null, sel, null, MyDb.TableName.TIMESTAMP);
MyDb is yours class defining tables and its fields, UriS etc.

Select every nth term including first from sqlite database

I am trying to select the first row and every fifth one after that (this gives me 4 queries with unique rows) but I am not sure that it is correct as my results are not as expected.
//create the nth row selection
int n = level + 5;
//query the content resolver
Cursor cursor = contentResolver.query(
MainContentProvider.CONTENT_URI_ENGLISH,
DatabaseOpenHelper.PROJECTION_ENGLISH,
DatabaseOpenHelper.KEY_ROW_ID + " = " + (level+1) + " OR " + DatabaseOpenHelper.KEY_ROW_ID +" % " + n +" = 0",
null,
DatabaseOpenHelper.KEY_ROW_ID + " ASC"
);
where level is in [0,3] interval.
The results do not add up to the expected value, and are twice as less (approximately).
How should my query method look like to select every fifth row starting from the first one?
You can achieve this using query like (I'm not sure about it's performance though):
SELECT
id,
name,
(SELECT count(*) - 1 FROM my_table t_inner WHERE t.id >= t_inner.id) AS ordinal
FROM my_table t
WHERE ordinal % 5 == 0
ORDER BY id ASC;
Or you can create CursorWrapper which will return subset of rows. The downside of the this approach is that you'll have to fetch every row from the database, which might be slow if you have a lot of data.
(Assuming that the row IDs are consecutive,) rowid % X = 0 selects every Xth row, so you must not change X.
If you cannot change that term, you have to change another.
Either apply an offset to the rowid values ((rowid - offset) % X = 0), or compare against another value (rowid % X = offset), where offset is in the range from 0 to X-1.
Please note that the first comparison (rowid = level+1) is not necessary.

Delete first N rows in android sqlite database

Please let me know how to delete n-rows in android sqlite database. I used this code:
String ALTER_TBL ="delete from " + MYDATABASE_TABLE +
"where"+KEY_ID+"in (select top 3"+ KEY_ID +"from"+ MYDATABASE_TABLE+"order by _id );";
sqLiteDatabase.execSQL(ALTER_TBL);
But it shows an error.
03-21 13:19:39.217: INFO/Database(1616): sqlite returned: error code = 1, msg = near "in": syntax error
03-21 13:19:39.226: ERROR/Database(1616): Failure 1 (near "in": syntax error) on 0x23fed8 when preparing 'delete from detail1where_id in (select top 3_idfromdetail1order by _id );'.
String ALTER_TBL ="delete from " + MYDATABASE_TABLE +
" where "+KEY_ID+" in (select "+ KEY_ID +" from "+ MYDATABASE_TABLE+" order by _id LIMIT 3);";
there is no "top 3" command in sqlite I know of, you have to add a limit
watch out for spaces when you add strings together : "delete from" + TABLE + "where" = "delete frommytablewhere"
This approach uses two steps to delete the first N rows.
Find the first N rows:
SELECT id_column FROM table_name ORDER BY id_column LIMIT 3
The result is a list of ids that represent the first N (here: 3) rows. The ORDER BY part is important since SQLite does not guarantee any order without that clause. Without ORDER BY the statement could delete 3 random rows.
Delete any row from the table that matches the list of ids:
DELETE FROM table_name WHERE id_column IN ( {Result of step 1} )
If the result from step 1 is empty nothing will happen, if there are less than N rows just these will be deleted.
It is important to note that the id_column has to be unique, otherwise more than the intended rows will be deleted. In case the column that is used for ordering is not unique the whole statement can be changed to DELETE FROM table_name WHERE unique_column IN (SELECT unique_column FROM table_name ORDER BY sort_column LIMIT 3). Hint: SQLite's ROWID is a good candidate for unique_column when deleting on tables (may not work when deleting on views - not sure here).
To delete the last N rows the sort order has to be reversed to descending (DESC):
DELETE FROM table_name WHERE unique_column IN (
SELECT unique_column FROM table_name ORDER BY sort_column DESC LIMIT 3
)
To delete the Nth to Mth row the LIMIT clause can be extended by an OFFSET. Example below would skip the first 2 rows and return / delete the next 3.
SELECT unique_column FROM table_name ORDER BY sort_column LIMIT 3 OFFSET 2
Setting the LIMIT to a negative value (e.g. LIMIT -1 OFFSET 2) would return all rows besides the first 2 resulting in deletion of everything but the first 2 rows - that could also be accomplished by turning the SELECT .. WHERE .. IN () into SELECT .. WHERE .. NOT IN ()
SQLite has an option to enable the ORDER BY x LIMIT n part directly in the DELETE statement without a sub-query. That option is not enabled on Android and can't be activated but this might be of interest to people using SQLite on other systems:
DELETE FROM table_name ORDER BY sort_column LIMIT 3
It seems that you've missed some spaces:
"where"+KEY_ID+"in..
must be:
"where "+KEY_ID+" in...
Furthermore you need to use the limit statement instead of top:
I'll do:
db.delete(MYDATABASE_TABLE, "KEY_ID > "+ value, null);
you can try this code
int id;
public void deleteRow(int id) {
myDataBase.delete(TABLE_NAME, KEY_ID + "=" + id, null);
}
String id;
public void deleteRow(String id) {
myDataBase.delete(TABLE_NAME, KEY_ID + "=\" " + id+"\"", null);
}
It is a bit long procedure but you can do it like this
first get the ids column of table from which which you want to delete certain values
public Cursor KEY_IDS() {
Cursor mCursor = db.rawQuery("SELECT KEYID " +
" FROM MYDATABASE_TABLE ;", null);
if (mCursor != null)
{
mCursor.moveToFirst();
}
return mCursor;
}
Collect it in an array list
ArrayList<String> first = new ArrayList<String>();
cursor1 = db.KEY_IDS();
cursor1.moveToFirst();
startManagingCursor(cursor1);
for (int i = 0; i < cursor1.getCount(); i++) {
reciv1 = cursor1.getString(cursor1
.getColumnIndex(DBManager.Player_Name));
second.add(reciv1);
}
and the fire delete query
for(int i = 0 ;i<second.size(); i++)
{
db.delete(MYDATABASE_TABLE KEYID +"=" + second.get(i) , null);
}
Delete first N (100) rows in sqlite database
Delete from table WHERE id IN
(SELECT id FROM table limit 100)
You can make use of the following mode: (in addition to the response provided by "zapl").
**DELETE FROM {Table-X} WHERE _ID NOT IN
(SELECT _ID FROM {Table-X} ORDER BY _ID DESC/ASC LIMIT (SELECT {Limit-Column} FROM {SpecificationTable}) );**
Where {Table-X} refers to the table you want to delete, _ID is the main unique-column
DESC/ASC - Based on whether you want to delete the top records or the last records, and finally in the "LIMIT" clause, we provide the "n" factor using another query, which calls in the {Limit-Column} from {SpecificationTable}: Which holds the value against which you want to delete them.
Hope this helps out someone.
Happy Coding.

SQLite order by 3 fields and return top 1 for each category

public Cursor fetchAllPBs() {
String sql = "SELECT * FROM " + CAPTURES_TABLE + " GROUP BY " + KEY_CAPTURES_SPECIES
+ " ORDER BY " + KEY_CAPTURES_POUNDS + " DESC, " + KEY_CAPTURES_OUNCES + " DESC, " + KEY_CAPTURES_DRAMS + " DESC;";
Cursor c = mDb.rawQuery(sql, null);
if(c != null) {
c.moveToFirst();
}
return c;
}
Hi,
I want the above query to return me the user's personal best for each species, that is, the heaviest item within each species. Testing it properly recently I've realised a problem. I'm still relatively new to SQL with this project...
Say I add a 'Chub' to my database of 7lb 6oz 0drms, then add another of 7lb 2oz 0drms - it will return the more recently added fish as the PB and not the biggest (the 7lb 2oz one). However if I then add another Chub of 8lb 0oz 0drms it will return the 8lb fish - it seems it's not properly ordering them by the Ounces and probably by that I assume the drams too.
Can anyone see what's wrong here and suggest a solution?
Many thanks
First, you need a subquery to determine the heaviest fish per species.
Second, your weight is split in 3 columns, so you need to add them in someway. I choose to just add them with multiplication, should be sufficient for getting the max.
SELECT *
FROM CAPTURES_TABLE AS C1
WHERE (100*Pounds+10*Ounces+Drams) =
(SELECT MAX(100*Pounds+10*Ounces+Drams)
FROM CAPTURES_TABLE AS C2
WHERE C2.SPECIES=C1.SPECIES)
ORDER BY pounds DESC, ounces DESC, drams DESC
More of a comment, but I need more space...
If I simplify your query it looks like:
SELECT * FROM table1 WHERE species = species_id
ORDER BY pounds DESC, ounces DESC, drams DESC LIMIT 1
Provided that species is not a unique field, the query is fine.
It looks like the problem is that when adding a new fish you are not adding, but actually replacing the fish, try and remove the limit 1 and see if all fish show up.
The ordering clauses are definitly correct.

Categories

Resources