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.
Related
I want to check if there's a record in my table Users corresponding to an id_user, in case there isn't I will add it. The problem is that my Cursor.getCount() returns 1 and it doesn't make sense because my table is completely empty.
Cursor c = db.rawQuery("SELECT count(*) FROM Users WHERE id_user = '"
+ jsonObj.getString("id_user") + "'", null);
Log.i("getUser cursor",c.getCount() + ""); // it prints 1
c.moveToFirst();
int ic = c.getInt(0);
Log.i("getUser count2", ic + ""); // it prints 0
Why c.getCount() is giving me 1 when there is not absolutely any record. However, c.getInt(0) seems to work fine.
Thanks
Because you are getting a row back in your query.
Select count(*)
will return one rows containing the count of records. The count of records is 0, thus it returns one row, containing the value 0.
Select *
then
c.getCount()
Would return the 0 you are expecting because you are pulling back all rows, (not a count of rows) and there are no rows. But this is a bad approach since it can pull back extra data and might be slow.
in this case
int ic = c.getInt(0); is the proper way to get the data you want.
I do big query with few subqueries and unions:
public Cursor getCursor(int outlayType, long carId){
String selection;
String[] selectionArgs;
if(outlayType>0){
selection = "car_id=? and type=?";
selectionArgs = new String[]{
Long.toString(carId),Integer.toString(outlayType),
Long.toString(carId),Integer.toString(outlayType),
Long.toString(carId),Integer.toString(outlayType)};
}else{
selection = "car_id=?";
selectionArgs = new String[]{Long.toString(carId), Long.toString(carId), Long.toString(carId)};
}
String sql = "select (select count(*)+1 from outlays b where a.date < b.date and "+selection+") as countNum," +
" id as _id, id, type, note, sum, date," +
" odometer, unread, future, input_type, 0 as row_type " +
" from outlays a where "+ selection +" " +
" union " +
" select 0, 0, 0, 0, '', sum(sum), max(date)+2," +
" 0, 0, 0, '', 1 as row_type" +
" from outlays where "+ selection +" group by strftime('%Y-%m', date/1000, 'unixepoch')" +
" order by 7 DESC";
return sqdb.rawQuery(sql, selectionArgs);
}
It works perfect, but ...
You can see: few times I use same WHERE conditions. And this is not the end. The query will grow. And this conditions will be use again and again.
I want to use temp table like this:
public Cursor getCursor(int outlayType, long carId){
String selection;
String[] selectionArgs;
if(outlayType>0){
selection = "car_id=? and type=?";
selectionArgs = new String[]{Long.toString(carId),Integer.toString(outlayType)};
}else{
selection = "car_id=?";
selectionArgs = new String[]{Long.toString(carId)};
}
sqdb.execSQL("drop table if exists sub");
sqdb.execSQL("create temp table sub " +
"as select * from outlays where "+selection, selectionArgs);
String sql = "select (select count(*)+1 from sub b where a.date < b.date) as countNum," +
" id as _id, id, type, note, sum, date," +
" odometer, unread, future, input_type, 0 as row_type " +
" from sub a " +
" union " +
" select 0, 0, 0, 0, '', sum(sum), max(date)+2," +
" 0, 0, 0, '', 1 as row_type" +
" from sub group by strftime('%Y-%m', date/1000, 'unixepoch')" +
" " +
" order by 7 DESC";
return sqdb.rawQuery(sql, null);
}
It looks better (for me), but when I call Cursor.notifyDataSetChanged - it works wrong. Because the recreation of temp table is not called.
How can I do one subquery or one temp table in the same query for Cursor?
I not sure what your ultimate goal is, but I was able to convert your first query into a correlated query. That is where the subselect gets if values for a column from the main query. The key is that the main query column MUST always be on the right side of the comparison operator in the subselect, so I reversed the dates and switch the comparison operator.
Using the following data as input:
1 1 1 note 1 1 20130601 6100 1 1 0 201306
2 2 2 note 2 2 20130701 72013 0 1 0 201307
3 3 3 note 3 3 20130715 4201 1 1 a 0 201307
4 3 3 note 4 4 20130318 68010 1 1 0 201303
5 1 1 note 5 5 20130615 37077 1 1 0 201306
I couldn't replicate the strftime with the tool is was using so I added the last column ym, which I believe is equivalent to your result of strftime for your group by.
Here's the SQL i came up with:
select (select count(*)+1 from outlays b where b.date1 > a.date1 and b.car_id=a.car_id) as countNum,
id as _id, a.id, a.type, a.note, a.sum1, a.date1, a.odometer, a.unread, a.future, a.input_type, 0 as row_type, ym
from outlays a where car_id= 1
union all
select 0, 0, 0, 0, '', sum(sum1), max(date1)+2, 0, 0, 0, ' ', 1 as row_type, ym from outlays group by ym order by 7 DESC;
There are the data results:
#| |.|.|.|. |.|. |. |.|.|.|.|.
-+-+-+-+-+------+-+--------+-----+-+-+-+-+------
1|0|0|0|0| |5|20130717|0 |0|0| |1|201307
2|0|0|0|0| |6|20130617|0 |0|0| |1|201306
3|1|5|5|1|note 5|5|20130615|37077|1|1| |0|201306
4|2|1|1|1|note 1|1|20130601|6100 |1|1| |0|201306
5|0|0|0|0| |4|20130320|0 |0|0| |1|201303
Note in this particular case until I really know the goal, either UNION or UNION ALL can be used.
Some things I noted and changed a few in the above:
There probably isn't a column name issue here 'id as _id and id' with the exception that you are almost being reduntant, in the future just use _id. Anyone maintaining the code will then understand it a bit quicker. Nice way to get around the _id requirement by android methods.
There is also an issue probably with a column names of 'sum', and 'date' since it is also a sql function or might be, or a reserved word in some language and if not, I would just avoid out of good database design.
FYI: Not that this one matters at all, but type isn't really descriptive, column names should be descriptive, so type should be type_ofwhat, but that only if you want to follow good database design.
In addition, since I didn't know your intent, I wasn't sure there were other columns that you would want to group by. From my experience, typically there are.
The two queries might be able to be one, again I'd need to know what the real intent is for the result and all columns therein.
Hope this helps.
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.
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.
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);
}
}