I have a query that selects rows in a ListView without having a limit. But now that I have implemented a SharedPreferences that the user can select how much rows will be displayed in the ListView, my SQLite query doesn't work. I'm passing the argument this way:
return wDb.query(TABELANOME, new String[] {IDTIT, TAREFATIT, SUMARIOTIT}, CONCLUIDOTIT + "=1", null, null, null, null, "LIMIT='" + limite + "'");
The equals (=) operator is not used with the LIMIT clause. Remove it.
Here's an example LIMIT query:
SELECT column FROM table ORDER BY somethingelse LIMIT 5, 10
Or:
SELECT column FROM table ORDER BY somethingelse LIMIT 10
In your case, the correct statement would be:
return wDb.query(TABELANOME, new String[] {IDTIT, TAREFATIT, SUMARIOTIT}, CONCLUIDOTIT + "=1", null, null, null, null, String.valueOf(limite));
Take a look here at the SQLite select syntax: http://www.sqlite.org/syntaxdiagrams.html#select-stmt
This image is rather useful: http://www.sqlite.org/images/syntax/select-stmt.gif
For anyone stumbling across this answer looking for a way to use a LIMIT clause with an OFFSET, I found out from this bug that Android uses the following regex to parse the limit clause of a query:
From <framework/base/core/java/android/database/sqlite/SQLiteQueryBuilder.java>
LIMIT clause is checked with following sLimitPattern.
private static final Pattern sLimitPattern = Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
Note that the regex does accept the format offsetNumber,limitNumber even though it doesn't accept the OFFSET statement directly.
Due to this bug which also doesn't allow for negative limits
8,-1
I had to use this workaround
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(table);
String query = builder.buildQuery(projection, selection, null, null, null, sortOrder, null);
query+=" LIMIT 8,-1";
Related
I'm trying to select some data from database and I have two slices of code to do it:
cursor = builder.query(db,
new String[]{"col1", "col2", "col3"},
"id = ?", new String[]{getSID(db)}, null, null, null);
and
cursor = builder.query(db,
new String[]{"col1", "col2", "col3"},
"id = " + getSID(db), null, null, null, null);
The difference between them is that first one seems to be more correct according to documentation, but it also doesn't work - cursor is empty. Instead of the second one - I'm getting all data I need.
So I tried to execute different SQL queries on my PC with a copy of database and that's what I've got:
SELECT col1, col2, col3 FROM SomeTables WHERE (id = '42')
This one doesn't work (and this query obviously equals to query, generated by first code sample)
SELECT col1, col2, col3 FROM SomeTables WHERE (id = 42)
And this one works fine (equals to query from second code sample).
As I know, SQLite should perform type cast automatically, but something went wrong and I don't know why. Do you have any ideas about how first code sample can be fixed? (Or, perhaps, database?)
If it matters, here's simplified CREATE script of the table with id field:
CREATE TABLE SomeTable ( ID PRIMARY KEY, col1, col2, [...] )
UPD: And, by the way, getSID(db) returns String Object.
That query parameters can only be strings is a horrible design error in the Android database API.
Despite what the documentation says, you should use parameters only for actual string values; integer values can be safely embedded directly into the SQL string. (For blobs, you must use a function that accepts ContentValues.)
Please note that while SQLite uses dynamic typing, values of different types do not compare equal in most cases (SELECT 42='42'; returns 0).
There are some cases where SQLite does automatically convert values due to type affinity (in your case, this would happen if you declared the id column as INTEGER), but this is rather counterintuitive, so it should not be relied upon.
According to SQLite documentation,
Any column in an SQLite version 3 database, except an INTEGER PRIMARY KEY column, may be used to store a value of any storage class.
In context of my case, that means that we can't be sure what data type will be stored in columns. If you can control and convert data types when they're putting into database - you can convert id values to TEXT when adding data to database and use selectionArgs easily. But it's not an answer for my question, because I have to deal with database content as is.
So, possible solutions:
a) embed integer values in selection string without wrapping them into ':
cursor = builder.query(db,
new String[]{"col1", "col2", "col3"},
"id = " + getSID(db), null, null, null, null);
b) cast values from selectionArgs: CAST(? as INTEGER) or CAST(id AS TEXT). I think, converting column to TEXT is better solution, because right operand is always TEXT, but the left one can be anything. So:
cursor = builder.query(db,
new String[]{"col1", "col2", "col3"},
"CAST(id AS TEXT) = ?",
new String[]{getSID(db)}, null, null, null);
You need to convert your int id into string before passing to your query because the parameter array is of type string. For example:
cursor = builder.query(db, new String[]{"col1", "col2", "col3"},
"id = ?", new String[]{String.valueOf(getSID(db))}, null, null, null);
The reason why it works in second type of query is because you are appending the integer value with string which automatically converts the int into String. For example:
int i = 10;
String s = i + ""; //now 10 is in string
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 am using the query method of SQLiteDatabase. I need help with the orderBy parameter of this method.
Cursor cursor = sqLiteDatabase.query(tableName, tableColumns, whereClause, whereArgs, groupBy, having, orderBy);
public Cursor getAllCustomexp(int TID) throws SQLException
{
Cursor mCursor =
db.query(false, CEXP_TABLE, new String[] {KEY_CEID, FLD_CETID, FLD_CEEID, FLD_CEMID,
FLD_CEAMT, FLD_CESEL}, FLD_CETID + " = " + TID, null,
null, null, FLD_CEEID, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
Question 1:
In the above query the result set will be sorted by FLD_CEEID in ascending or descending order ?
Question 2:
If I need to order the result set first by FLD_CEEID and then by FLD_CEMID how should i construct the order by parameter of this query.
Is it possible to do multiple order by using this method?
From SQLite docs:
If a SELECT statement that returns more than one row does not have an
ORDER BY clause, the order in which the rows are returned is
undefined. Or, if a SELECT statement does have an ORDER BY clause,
then the list of expressions attached to the ORDER BY determine the
order in which rows are returned to the user. Rows are first sorted
based on the results of evaluating the left-most expression in the
ORDER BY list, then ties are broken by evaluating the second left-most
expression and so on. The order in which two rows for which all ORDER
BY expressions evaluate to equal values are returned is undefined.
Each ORDER BY expression may be optionally followed by one of the
keywords ASC (smaller values are returned first) or DESC (larger
values are returned first). If neither ASC or DESC are specified, rows
are sorted in ascending (smaller values first) order by default.
Answer 1: Result set will be sorted in ascending order.
Answer 2:
String orderBy = FLD_CEEID + " ASC, " + FLD_CEMID + " ASC";
db.query(false, CEXP_TABLE, new String[] {KEY_CEID, FLD_CETID, FLD_CEEID, FLD_CEMID,
FLD_CEAMT, FLD_CESEL}, FLD_CETID + " = " + TID, null,
null, null, orderBy, null);
It works like in SQL Select Order By. You can find details here: http://en.wikipedia.org/wiki/Order_by_(SQL) .
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.
I'm getting an annoying error when trying to query some data in SQLite.
Here is my code:
Cursor cursor= db.query(TABLE_IMAGES, new String[]{"_id"}, "name" +" = "+compareToThis, null, null, null, null);
I'm just returning the cursor as a string.
The error is saying:
no such column: compareToThis: while compiling.....the statement
My question is: why is SQLite setting the compareToThis attribute as a column when it's just a value?
How can I fix this?
Thanks in advance.
Cursor cursor= db.query(TABLE_IMAGES, new String[]{"_id"}, "name" +" = ?", new String[]{compareToThis}, null, null, null);
The selection must include placeholder for parameter, and the next argument should be the array of parameters.
The solution by Vladimir works, however if you are like me and wonder why your approach did not work initially when it should have, here is why:
It is because it expects an integer unless you used (single or double) quotation marks to indicate that it is a string.
For example, in MySql this would return no results:
SELECT * FROM clients WHERE firstName = Bob; -- This will not work.
However when you surround it with quotations, it will return a result because it identifies Bob as a String literal.
Select * FROM clients WHERE firstName = 'Bob'; -- Single quotes work.
Select * FROM clients WHERE firstName = "Bob"; -- Double quotes as well.
Therefore for it to work, you would have to surround your compareToString with single quotes, as Muhhammad mentioned within the comments.
Cursor cursor= db.query(TABLE_IMAGES, new String[]{"_id"}, "name" +'" = "+compareToThis+"'", null, null, null, null);