I query and get a result set back, but I need to do some calculations that are impossible in the SQLite WHERE clause in order to determine what shows up in the ListView. How can I remove certain rows from the cursor? I know it is the same question as this Filter rows from Cursor so they don't show up in ListView but that answer does not help. Can an example be provided if there isn't a simpler way to do this?
It might work to simply retain all the rows in the Cursor, but then use a custom adapter to hide the unwanted rows at display time. For example, if you extend CursorAdapter, then you might have something like this in your bindView implementation:
View v = view.findViewById(R.id.my_list_entry);
boolean keepThisRow = .......; // do my calculations
v.setVisibility(keepThisRow ? View.VISIBLE : View.GONE);
There should be a better way to do this, but what I ended up doing is storing the ID of each row I wanted in a string ArrayList, and then requerying where _id IN arraListOfIds.toString(), replacing the square brackets with parentheses to fit SQL syntax.
// Get all of the rows from the database
mTasksCursor = mDbHelper.fetchAllTasks();
ArrayList<String> activeTaskIDs = new ArrayList<String>();
// calculate which ones belong
// .....
if (!hasCompleted)
activeTaskIDs.add(mTasksCursor.getString(TaskerDBadapter.INDEX_ID));
// requery on my list of IDs
mTasksCursor = mDbHelper.fetchActiveTasks(activeTaskIDs);
public Cursor fetchActiveTasks(ArrayList<String> activeTaskIDs)
{
String inClause = activeTaskIDs.toString();
inClause = inClause.replace('[', '(');
inClause = inClause.replace(']', ')');
Cursor mCursor = mDb.query(true, DATABASE_TABLE, columnStringArray(),
KEY_ROWID + " IN " + inClause,
null, null, null, null, null);
if (mCursor != null) { mCursor.moveToFirst(); }
return mCursor;
}
ContentResolver cr = getContentResolver();
Cursor groupCur = cr.query(
Groups.CONTENT_URI, // what table/content
new String [] {Groups._ID, Groups.NAME}, // what columns
"Groups.NAME NOT LIKE + 'System Group:%'", // where clause(s)
null, // ???
Groups.NAME + " ASC" // sort order
);
The "What Columns" piece above is where you can tell the cursor which rows to return. Using "null" returns them all.
I need to do some calculations that
are impossible in the SQLite WHERE
clause
I find this very hard to believe; my experience has been that SQL will let you query for just about anything you'd ever need (with the exception of heirarchical or recursive queries in SQLite's case). If there's some function you need that isn't supported, you can add it easily with sqlite_create_function() and use it in your app. Or perhaps a creative use of the SELECT clause can do what you are looking for.
Can you explain what these impossible calculations are?
EDIT: Nevermind, checking out this webpage reveals that the sqlite_create_function() adapter is all closed up by the Android SQLite wrapper. That's annoying.
Related
This is a general performance question, I have no error or something like this.
I am working on an app which uses a SQLite Database, and just want to know what is the best, fastest and most efficient way to query through a table to find a special value.
Example:
I have a table and I am searching for a special string.
I get all rows by:
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
and then iterate through the cursor like
String searchedString = "THIS_IS_JUST_AN_EXAMPLE_PLEASE_IGNORE_TYPING_ERROR";
boolean success = false;
int count = cursor.getCount();
for(int i=0;i<count;i++) {
String queryString = c.getString(1);
if(queryString.equals(searchedString) {
success=true;
break;
} else {
cursor.moveToNext();
}
}
Another possible way would be to use query():
Cursor cursor = db.query(
TABLE_NAME, new String[] {STRING_COL},
STRING_NAME + "=?",
new String[] {"THIS_IS_JUST_AN_EXAMPLE_PLEASE_IGNORE_TYPING_ERROR"},
null, null, null, null);
Is there a performance difference between these two methods?
A lot of tutorials out there are showing the iteration via loop, but the Docs recommend to use the query() method.
Is the query() method the same as iterating through a loop?
How exactly does it work? I can't find in any API.
Doing the search by hand requires more code (which increases the risk of errors).
The DB probably executes a similar loop, but doing the search in the DB does not require all of the data be moved from the DB to your application.
If there is not much data, there will not be any noticeable performance difference between the two algorithms. However, when the amount of data becomes larger, you can speed up the SQL search by simply creating an index.
The only difference I can spot is the WHERE part, which lacks in the first algorithm.
The first algorithm will benefit a lot, if you add a WHERE clause to the query. And then become identical to the second algorithm, in terms of performances.
Something like
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_NAME + " WHERE STRING_COL = ?", new String[]{"THIS_IS_JUST_AN_EXAMPLE_PLEASE_IGNORE_TYPING_ERROR"});
As it is right now, the first algorithm is slower.
As noted bt #CL, both the algorithms can be drammatically improved by indexing the WHERE column.
There is android sqlite database with one table, im using this query for getting values :
Cursor cursor = db.query(Table_Items, null, "type=? AND operationtype=? AND problemtype=?",
new String[] { roosazi,type,problem }, null, null,KEY_Items_ID+" "+date , null);
everything is working fine.
question is:
how can i get all from specific column? let me give an example:
all values with type="A" and operationtype="XYZ" are needed , no matter what problemtype is! of course i can use something like this :
Cursor cursor = db.query(Table_Items, null, "type=? AND operationtype=?",
new String[] { roosazi,type }, null, null,KEY_Items_ID+" "+date , null);
but problem is sometimes problemtype is X and sometimes its like ALL!
how can i achieve this? can i put something like * instead of problem?
thank u so much
I understand that you want to use the same query to get sometimes with a particular problem type like X,Y or Z, and sometimes with any problem type,
If that is the case you could use the statement 'like' in your query instead of '=' in the problemtype field
Cursor cursor = db.query(Table_Items, null, "type=? AND operationtype=? AND problemtype like '?'",
When you want to return all values just past
problem = "%"
% simbol means any characters,
When you want to return values with a particular problem use
problem = "X"
in the values array
new String[] { roosazi,type,problem }
I use CursorLoader to query a result, which is not the order that I want to show in the ListFramgenet. How to sort it ?
I use this to set the adapter:
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] { "name", "distance"},
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
Create loader :
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(),
Uri.withAppendedPath(TraceTable.CONTENT_URI, "latest"),
MEMBERS_PROJECTION,
null,
null,
null);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.changeCursor(data);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
Well, there are latitude and longitude the queried results. I want to calculate the distance between my location and these results. and sort by distance asc.
How to sort it? Any answer will be appricated
It's actually quite easy:
from this:
new CursorLoader(getActivity(),
Uri.withAppendedPath(TraceTable.CONTENT_URI, "latest"),
MEMBERS_PROJECTION,
null,
null,
null);
to this:
// You could have them calculated in the projection like this:
String[] projection = { COLUMN1 + " * " + COLUMN2 + " as data", // Whatever your calculations are
COLUMN1, COLUMN2, COLUMN3, etc.. };
new CursorLoader(getActivity(),
Uri.withAppendedPath(TraceTable.CONTENT_URI, "latest"),
projection,
null,
null,
"data ASC");
Remember that if you have some method in your provider that does a check to the
projection and rise an exception, you would have to comment it out for the moment you are doing the test or add the new column (the one you do the calculation with) to your official projection array.
What you're looking for is supplying a sortOrder argument something like the following:
(the ORDER BY at the start of this is implicait and you do not include it, simply included here for clarity)
ORDER BY 6366000*acos(cos(lat_a / (180/3.14169))*cos(lng_a / (180/3.14169))*cos(lat_b / (180/3.14169))*cos(lng_b / (180/3.14169)) + t2 + t3) ASC
(for this I took the answer here and inlined it - except I didn't bother with t2 and t3, because it's not going to work anyway)
Unfortunately, this is impossible in standard SQLite - there are no operators sin, or cos - or even a square root or power operator (after seeing your comment about only requiring a more simple calculation).
You can add your own functions, but that's a somewhat more complicated route.
Depending on how many rows you have, you might be alright to just read them all and then sort them yourself.
Provide the column names you want to sort on as the last parameter of the CursorLoader() constructor as a string. If you have more than one column to sort by, separate with a comma. If you want ascending as opposed to descending, add DESC after the column name. So pretty much what you would add after 'ORDER BY' in regular SQL syntax.
EDIT: To answer your comment below.
Yes and no. I believe you could use that as your sort order but SQLite does not have a sqrt or power function. However you can define your own SQLite functions or use a 3rd party extension. If you don't want to go that route, you'll have to use your own custom adapter instead of SimpleCursorAdapter, e.g. you would get the cursor results and then sort them in code into another data stucture which would become the datasource of your adapter.
Just add ContactsContract.Contacts.SORT_KEY_PRIMARY.
I'm writing a method to update default settings in a table. The table is very simple: two columns, the first containing labels to indicate the type of setting, the second to store the value of the setting.
At this point in the execution, the table is empty. I'm just setting up the initial value. So, I expect that this cursor will come back empty. But instead, I'm getting an error (shown below). The setting that I am working with is called "lastPlayer" and is supposed to get stored in the "SETTING_COLUMN" in the "SETTINGS_TABLE". Here's the code:
public static void updateSetting(String setting, String newVal) {
String table = "SETTINGS_TABLE";
String[] resultColumn = new String[] {VALUE_COLUMN};
String where = SETTING_COLUMN + "=" + setting;
System.err.println(where);
SQLiteDatabase db = godSimDBOpenHelper.getWritableDatabase();
Cursor cursor = db.query(table, resultColumn, where, null, null, null, null);
System.err.println("cursor returned"); //I never see this ouput
\\more
}
sqlite returned: error code = 1, msg = no such column: lastPlayer
Why is it saying that there is no such column lastPlayer? I thought that I was telling the query to look at the column "SETTING_COLUMN" and return the record where that column has a value "lastPlayer". I'm confused. Can somebody straighten me out? I've been looking a this for an hour and I just don't see what I am doing wrong.
Thanks!
You're not properly building/escaping your query. Since the value lastPlayer is not in quotes, your statement is checking for equality of two columns, which is what that error message is saying.
To properly build your query, it's best to not do this manually with String concatenation. Instead, the parameter selectionArgs of SQLiteDatabase.query() is meant to do this.
The parameters in your query should be defined as ? and then filled in based on the selectionArgs. From the docs:
You may include ?s in selection, which will be replaced by the values
from selectionArgs, in order that they appear in the selection. The
values will be bound as Strings.
So, your code would look like this:
String where = SETTING_COLUMN + " = ?";
Cursor cursor = db.query(table, resultColumn, where, new String[] { setting }, null, null, null);
I have this function that is filling out a class based on data from several tables. I got the first cursor:
String query="SELECT * FROM SESSION where _id =" + mSessionID + ";";
Cursor c = dbAdapter.selectRecordsFromDB(query, null);
Session session=null;
c.moveToFirst();
This works great. Then a little lower I do this:
long galleryId = c.getInt(4);
long packageId = c.getInt(5);
long contractId = c.getInt(6);
String query2="SELECT * FROM PHOTOPACKAGES WHERE _id =" + packageId + ";";
Cursor p = dbAdapter.selectRecordsFromDB(query2, null);
and the p cursor always returns -1 for its count. I can go right into the sqlite in the adb and run the same query where packageId = 1 and it works great...so not sure why this is not working, i don't see any other errors...can you just not use two cursors on the same database? p.s. selectRecordsFromDB is a helper function:
public Cursor selectRecordsFromDB(String query, String[] selectionArgs) {
Cursor c = myDataBase.rawQuery(query, selectionArgs);
return myDataBase.rawQuery(query, selectionArgs);
}
To answer your actual question: Yes you can target the same DB with multiple cursors. I believe there is something else wrong with your code.
Also as Philip pointed out, creating Cursors is very costly and you do not want to make extras just because, and always close them when finished with them.
Your selectRecordsFromDB function looks pretty darned weird, but it will probably work after a fashion, because the first cursor that you create goes out of focus straight away. Leaking open cursors like that is not a good idea though.