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.
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.
I am still a newbie in android developing and have a problem with my ToDo application.
At the moment my listview displaying all saved ToDo's is only sorted by the first two digits (the days!) of the date but the date is stored in this format: "dd.mm.yyyy"
My target is to sort my listview in ascending order by date (ToDoTable.COLUMN_TODO_START) and time (ToDoTable.COLUMN_TIME_START).
I use the following code at the moment:
public void fillData() {
String[] from = new String[] { ToDoTable.COLUMN_TODO_NAME, ToDoTable.COLUMN_TODO_PLACE, ToDoTable.COLUMN_TODO_START, ToDoTable.COLUMN_TODO_END };
int[] to = new int[] { R.id.todo_name, R.id.todo_place, R.id.todo_start };
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.todo_row_test2, null, from,
to, 0);
setListAdapter(adapter);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { ToDoTable.COLUMN_ID, ToDoTable.COLUMN_TODO_NAME, ToDoTable.COLUMN_TODO_PLACE, ToDoTable.COLUMN_TODO_START, ToDoTable.COLUMN_TIME_START, ToDoTable.COLUMN_TODO_END, };
CursorLoader cursorLoader = new CursorLoader(this,
DataContentProvider.CONTENT_URI, projection, null, null, ToDoTable.COLUMN_TODO_START + " ASC" );
return cursorLoader;
}
I would be very grateful if someone can help me with this issue especially in the point to sort the list by both criteria (date and time).
Thanks in advance.
Best solution would be a database: For lots of data, feel free to use mysql, e.g. sqlite3. Import the library, put all data in the table and run a query:
reslt =mydatabase.find("select name,todo,time_start,time_end from todo_list order by time_start")
In mysql, the data and time is just one field. Use the TIMESTAMP in sql.
SQLite does not have a storage class set aside for storing dates and times. You can use Date And Time Functions.
So raw query can look like:
SELECT * FROM Table ORDER BY datetime(dateColumn) DESC Limit 1
I have the following two queries:
A.
db.query(DBHelper.TABLE, new String[] {
DBHelper._ID, DBHelper._DATE_AND_TIME,
DBHelper._SOURCE, DBHelper._MODE,
"SUM(" + DBHelper._AMOUNT + ")" },
DBHelper._DATE_AND_TIME + " BETWEEN ? AND ?",
new String[] { date_min, date_max }, null, null, null, null);
and result of sum goes to textview like this
String.valueOf(cursor.getString(4)).
Second query B.
db.query(DBHelper.TABLE, new String[] {
DBHelper._ID, DBHelper._DATE_AND_TIME,
DBHelper._SOURCE, DBHelper._MODE,
DBHelper._AMOUNT }, DBHelper._DATE_AND_TIME
+ " BETWEEN ? AND ?", new String[] { date_min, date_max },
null, null, null, null);
and result goes to
adapter = new SimpleCursorAdapter(this, R.layout.item, cursor, from,
to, FLAG_AUTO_REQUERY);
What I want is to combine both queries. Close cursor after first query and to use same query (A) for adapter. So far I have added DBHelper._AMOUNT to SELECT of A query but ListView shows only the last entry result (not the whole data). How can I modify query A for showing SUM in TextView and then use same query for adapter.
In a normal query (like B), the database returns a result record for each table record that matches the WHERE filter.
However, when you are using an aggregate function like SUM(), the database computes a single value from all table records (or from all records in a group if you're using GROUP BY) and returns that as a single result record.
Therefore, your two queries are fundamentally different and cannot be combined into a single query.
(Please note that the first four result columns of your query A do not have any meaningful value because the result record is not guaranteed to correspond to any particular record in the original table; you should ask only for the SUM value.)
I am using a spinnerbox in my application. The spinnerbox is to be filled with projects from the database. This itself already works. However i need one extra item in the drop down list.
I want the first item to be "general" general is not a project. Thus it is not retrieved from the database. Is there someway to either inject it in thye cursor or adapter?
What worked for me was to do a UNION in the sql query.
dbStatic.rawQuery(
" SELECT 2 as deftop, typeid as _id, typename as label FROM objtypes UNION "+
" SELECT 1 as deftop, -1 as _id, "+strDefaultSpinner+" as label "+
" ORDER BY deftop asc, label ", null
);
if the item selected is -1, then it's the default value. Otherwise it's a record from the table.
I encountered the same problem a while ago ..
the problem is that you cant actually insert information into a cursor (because its just a pointer) so I believe you have to have some kind of mediator in between ..
my way of solving it was to simply crate a string array [cursor.getCount+1]
then insert your "general" in [0] and then go through your cursor to insert the rest ..
it does go through the items an extra round (which isn't so bad in my case) but for a long list you might want to override the adaptar and insert a line before it goes through the cursor which i cannot help you with the code for that..
I managed to solve this in a differebt way then i originally planned. But it works well. Instead of a general option. I made a checkbox. Is it checked, then its general and the spinner is setunabled. And if unchecked it gets set to enabled. This works for my situation.
My example is working with androidx, room and spinner component.
In your content provider you should have something like.
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection,
#Nullable String[] selectionArgs, #Nullable String sortOrder) {
...
String all = context.getResources().getString(R.string.search_spinner_all);
SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT '"+all+"' as "+ThemeData.COLUMN_ID+",'' as "+ThemeData.COLUMN_SET_COUNT+",0 as "+ThemeData.COLUMN_SUBTHEME_COUNT
+",0 as "+ThemeData.COLUMN_YEAR_FROM+",0 as "+ThemeData.COLUMN_YEAR_TO
+" UNION SELECT * FROM " + ThemeData.TABLE_NAME+" ORDER BY "+ ThemeData.COLUMN_ID);
cursor = themeDataDao.selectAll(query);
...
}
In your dao, use
#RawQuery
#Dao
public interface ThemeDataDao {
#RawQuery
Cursor selectAll(SupportSQLiteQuery query);
}
You got it, you can use your simple implementation or cursor adapter !
themesAdapter = new SimpleCursorAdapter(getContext(), R.layout.spinner_with_count, null,
new String[]{ThemeData.COLUMN_ID, ThemeData.COLUMN_SET_COUNT}, new int[] { R.id.spinnerTxLabel, R.id.spinnerTxCount }, 0);
inputTheme.setAdapter(themesAdapter);
LoaderManager.getInstance(this).initLoader(LOADER_THEMES, null, themesLoaderCallback);
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.