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);
Related
I've spent the whole day so far trying to get a select query to execute viarawquery or query, but I've had no luck so far.
The select statement I want to run is as the following:
SELECT * FROM h_word WHERE category='GRE' AND DONE=0 ORDER BY RANDOM() LIMIT 1
category is a TEXT type column and DONE is an INTEGER type with the default value of 0.
While the query works fine when executed directly in SQLite, in android,it doesn't return any results.
I've tried the below with no luck (the method is located in a class extended from SQLiteAssetHelper which itself is a helper class originally extended from SQLiteOpenHelper originaly taken from here: https://github.com/jgilfelt/android-sqlite-asset-helper:
public Cursor getRandomWord() {
Cursor c;
SQLiteDatabase db = getWritableDatabase();
c=db.rawQuery(query, null);
String query = "SELECT * FROM h_word WHERE category='GRE' AND DONE='0'
ORDER BY RANDOM() LIMIT 1 ";
c=db.rawQuery(query, new String[] {});
c.moveToFirst();
db.close();
return c;
}
I also tested with GRE instead of 'GRE' and 0 instead of '0' but it made no difference.
did the following as well:
public Cursor getRandomWord() {
Cursor c;
SQLiteDatabase db = getReadableDatabase();
c=db.query(true, "h_word", new String[] {
"_id",
"word",
"english_meaning"
},
"category" + "=?" + " AND " +
"DONE" + "=?",
new String[]{"GRE" ,"0"},
null, null, "RANDOM() LIMIT 1" , null);
c.moveToFirst();
db.close();
return c;
}
but the cursor remains empty.
Any ideas what I might be doing wrong here?
Any help would be much appreciated.
PS: when running a simple select statement without a where clause it, works fine.
After another few hours of struggling, I figured it's a bug in android's SQLiteDatabase class.
I managed to solve the problem by changing the name of the "category" column to something else.
Seems like "category" is a key word in the android SQLiteDatabase code, and makes a query return nothing when written in where clauses on the android side.
Someone else also had this problem here:
Android rawquery with dynamic Where clause
I designing a custom ListView ,which has more child view
I have ideas about sorting the ListViewdata in "Asc" or "Desc" order ,that retrieves data directly from database , but in my case I used CustomSimpleCursorAdapter , I requires to sort data in TextView depending upon the values that is:
today
tomorrow
more than 2 days i.e; 354
CustomSimpleCursorAdapter .java
//Days remaining for BirthDay
String year=cursor.getString(cursor.getColumnIndex(BirthdayProvider.EVENT_YEAR));
String month=cursor.getString(cursor.getColumnIndex(BirthdayProvider.EVENT_MONTH));
String date=cursor.getString(cursor.getColumnIndex(BirthdayProvider.EVENT_DATE));
String remainingDays=BirthdayCalculation.getDaysRemainingForNextBirthDay(year, month, date);
Calendar today=Calendar.getInstance();
int CMonth=(today.get(Calendar.MONDAY)+1);
int CDate=(today.get(Calendar.DAY_OF_MONTH));
//checking whether the BD is on TODAY
if (remainingDays.equals("1") && (CDate==Integer.parseInt(date) && (CMonth)==Integer.parseInt(month))) {
viewHolder.txtDaysRemainigValue.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
viewHolder.txtDaysRemainigValue.setTypeface(fontRoboto_Regular);
viewHolder.txtDaysRemainigValue.setTextColor(Color.parseColor("#00CC33"));
remainingDays="today";
}
//checking whether the BD is on TOMORROW
else if (remainingDays.equals("1")) {
viewHolder.txtDaysRemainigValue.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);
viewHolder.txtDaysRemainigValue.setTextColor(Color.parseColor("#FF0099"));
remainingDays="tomorrow";
viewHolder.txtDaysRemainigValue.setTypeface(fontRoboto_Regular);
}
//checking how many days remaining BD
else{
remainingDays=BirthdayCalculation.getDaysRemainingForNextBirthDay(year, month, date);
viewHolder.txtDaysRemainigValue.setTextSize(TypedValue.COMPLEX_UNIT_SP, 27);
viewHolder.txtDaysRemainigValue.setTextColor(Color.parseColor("#990000"));
}
Here's Screen Shot link
When you query your database, you should use an "order by" clause. For example, this method takes the order by clause as the last argument. I don't know how you store your dates and times in your database, but if it's something SQLite can recognize and provide sorting on, then it should work. The following will query for all columns on a table named "table" with no where clause, no "group by" clause, no "having" clause, and order by the time column descending (use ASC for ascending if you want that instead):
database.query("table", null, null, null, null, null, "time DESC");
EDIT
If you can't store the exact data you want (in this case days remaining until an event), I can only see two options:
1). After getting the cursor, you iterate over the results and compose a new sorted list. You could make some kind of model object in java, read the values into it from the cursor, and sort them with a comparator function. At that point you probably would not use a CursorAdapter any more. It's quite easy to build your own ListAdapter - I recommend you watch The World of Listview
2). Since the query methods take strings, you can actually compose more complicated queries so that SQLite provides you the data you DO want (and still sort it for you as well). If your times are stored as longs, you could do something like this:
long currentTime = System.currentTimeMillis();
String timeAsString = Long.toString(currentTime);
String remainingTimeColumn = "(time_column - " + timeAsString + ") AS remaining_time";
// compose query
String table = "table";
String[] columns = new String[] {"column1", "column2", ..., "columnN", remainingTimeColumn};
String order = "remaining_time ASC";
// query
Cursor cursor = database.query(table, columns, null, null, null, null, order);
// later, get remaining time from cursor row
long remainingTime = cursor.getLong(cursor.getColumnIndex("remaining_time"));
In this case "time_column" is the name of the column that stores the event time. What this is doing is creating an additional column with the name "remaining_time", which is calculated from one of the actual column values. The results are then sorted by that synthesized column. The cursor you get back will contain this column and these values, you just need to have the proper column name to access them.
Since I don't know the details of your data format, I can't say this is exactly how your query should look, but the idea should be clear. You can work out the finer details from here...
If you get the data in database, you can use ASC or DESC and put ArrayList.
another way is you can use Collections.sort(). but you must data class implements comparable and overriding compare method.
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 was trying to solve the question on why I was getting this error yesterday with some code:
java.lang.IllegalArgumentException: column '_id' does not exist
I had a lot more code, especially that I did not need, so I stripped a lot of it out to make it easier to understand where I am going wrong. But essentially this is my schema:
database.execSQL("CREATE TABLE events (" +
"_id INTEGER PRIMARY KEY, event_name TEXT" +
")");
As one can tell, looks fine right.
Unless I forgot to read, it's most obviously there. But then I figured out where my error was coming from, or at least I am sure this is why. This code that retrieves a cursor:
public Cursor getEventsName() {
return database.rawQuery( "SELECT event_name FROM events", null);
}
According to android, this is the error. When I change it to this:
public Cursor getEventsName() {
return database.rawQuery( "SELECT * FROM events", null);
}
Everything is peachy. When the former, it crashes. Any reason as to why this is. I thought that in rawQuery() I could do that. So long as I am not including where clauses, which I am not. Any help much appreciated.
Let's call these, event cursor:
public Cursor getEventsName() {
return database.rawQuery( "SELECT event_name FROM events", null);
}
... and * cursor:
public Cursor getEventsName() {
return database.rawQuery( "SELECT * FROM events", null);
}
Most of the answers that you have received (even the ones here: In Android, does _id have to be present in any table created?) are guessing at the likely cause for your error. I figured I would answer your question as well:
Any reason as to why (the former crashes and the later is peachy?)
The difference between the * and event cursors is that * is selecting every column implicitly and event is only selecting event_name. In your events table, the * cursor is the equivalent of:
SELECT _id, event_name FROM events;
which is why the this cursor works just peachily. In other words you are not receiving this error:
java.lang.IllegalArgumentException: column '_id' does not exist
because you are implicitly selecting the _id column with *.
Of course the most probable reason for getting this error is when you bind your data with a ListView, Spinner, etc; they all tend to use a CursorAdapter of some form. This is from the CursorAdapter documentation:
Adapter that exposes data from a Cursor to a ListView widget. The Cursor must include a column named "_id" or this class will not work.
So the Solution is simple: you must select the _id column in your query as well as the other columns that you want. (The compiler isn't lying to you.)
That being said, if this still doesn't seem valid to your app or doesn't make sense please post the code where you use the Cursor and the error is thrown.
I suspect that whatever was handling the cursor was trying to get the _ID column but it wasn't specified in your select statement. Doing something like,
public Cursor getEventsName() {
return database.rawQuery( "SELECT _id, event_name FROM events", null);
}
Some Android components, such as the SimpleCursorAdapter require the _ID be available in the select statement since it uses internally when getItemId() is called.
java.lang.IllegalArgumentException: column '_id' does not exist
I had same problem, this exception is thrown because SimpleCursorAdapter need for SELECT column named _id so you can resolve it when for example if you created some table with column KEY_ID as PK so you can try it like this:
SELECT KEY_ID AS _id, column1, column2 FROM SomeTable.
public Cursor getEventsName() {
return database.rawQuery( "SELECT * FROM events", null);
Change it to
public Cursor getEventsName(){
final String[] columns = new String[]{"_id", "event_name "};
return database.query(events, columns, "" , null, null, null, null);
}
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.