How query SQL with a user selected order - android

I'm trying to set up a user-selected SQL Query in Android Room.
#Query("SELECT * FROM wrestler_table ORDER BY :columnName :sortOrder LIMIT :size")
LiveData<List<WrestlersEntity>> getAllWrestlers(String columnName, String sortOrder, int size);
Android stuido is listing the sortOrder in the query as an error.
ASC, BETWEEN, COLLATE, DESC, IN, LIMIT, comma or semicolon expected, got ':sortOrder'
I want the sortOrder to be a variable so the user can change the order the data is returned in. Any thoughts on what I'm doing wrong here?

Well, you cannot pass the sort direction as query parameter. Not only SQLite, but most RDBMS do not allow this.
The following expression should do the trick:
ORDER BY
CASE WHEN :sortOrder = 'DESC' THEN :columnName END DESC,
CASE WHEN :sortOrder = 'ASC' THEN :columnName END ASC
LIMIT :size"
Alternatively, you can also concatenate the sort direction directly in the query string instead of passing it as a parameter.

Related

Sqlite: SqliteDatabase.delete() vs a raw query

Conclusion: Android's database APIs work but the documentation is horribly incomplete.
I have recently run into a brain wrecking situation due to the flexibility Sqlite provides by not forcing you to specify the data type when creating the table. The problem was my mindset that assumed that every data type would be a general character sequence if not specified and therefore the way to talk to database is through java.lang.String.
But you can't blame me either when you see methods like the below:
int delete (String table,
String whereClause,
String[] whereArgs)
in the SqlDatabase class from Android docs.
I have a table consisting of Phone No(that I stored as java.lang.String) and Timestamp as a long field. When I tried deleting a record using this method, it just never got deleted despite countless debugging.
I checked everything and query was alright and table is existent and all the checklist until by chance, I discovered that removing the '' around the timestamp while querying in a raw manner instead of using the above method yields a successful deletion, something like this:
DELETE FROM messages_records_table WHERE messageTimestamp = 1508494606000;
instead of the following:
DELETE FROM messages_records_table WHERE messageTimestamp = '1508494606000';
or,
DELETE FROM messages_records_table WHERE messageTimestamp = "1508494606000";
Phone No isn't a problem; it's the timestamp that was creating the problem in INSERTION/DELETION
So, I tried running a raw deletion query with quotes removed(that are required with a string/varchar type) and it yielded successful deletion. I used the following method for this:
db.execSQL(String sql, Object[] whereArgs)
The key thing to notice here is that Object[] is different from String[] when compared to delete(). I passed a Long to Object to make it work but passing a Long.toString() in delete() seems to be useless.
So my question is, Is my analysis correct and delete() API is basically useless or have I missed some bigger picture..after all, it's provided by Android team carefully?
SQLite supports multiple data types; and while column types are not strictly enforced, values might be automatically converted in some cases (this is called affinity).
When your values are stored as numbers, you should access them as numbers, not as strings.
The Android database API does not allow you to use parameter types other than strings in most functions. This is a horrible design bug.
To search for a number, either use execSQL(), which allows you to use number parameters, or convert the string value back into a number:
db.delete(..., "timestamp = CAST(? AS NUMBER)",
new String[]{ String.valueOf(ts) });
The problem was my mindset that assumed that every data type would be
a general character sequence if not specified and therefore the way to
talk to database is through java.lang.String.
I think that's the real issue.
If you specify no type e.g.
CREATE TABLE mytable (col1,col2,col3)
Then according to Determination of Column Affinity(3.1) rule 3:-
3) If the declared type for a column contains the string "BLOB" or if no
type is specified then the column has affinity BLOB.
And then according to Section 3
A column with affinity BLOB does not prefer one storage class over
another and no attempt is made to coerce data from one storage class
into another.
I've personally never had an issue with delete. However I do have a tendency to always delete according to rowid.
Here's a working example usage that shows that delete isn't useless and is deleting according to a long. However the columns are all of type INTEGER :-
int pudeletes;
int sldeletes;
int rdeletes;
int pdeletes;
if(doesProductExist(productid)) {
// if not in a transaction then begin a transaction
if(!intransaction) {
db.beginTransaction();
}
String whereargs[] = { Long.toString(productid)};
// Delete ProductUsage rows that use this product
pudeletes = db.delete(
DBProductusageTableConstants.PRODUCTUSAGE_TABLE,
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete ShopList rows that use this product
sldeletes = db.delete(
DBShopListTableConstants.SHOPLIST_TABLE,
DBShopListTableConstants.SHOPLIST_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete Rules rows that use this product
rdeletes = db.delete(
DBRulesTableConstants.RULES_TABLE,
DBRulesTableConstants.RULES_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete the Product
pdeletes = db.delete(
DBProductsTableConstants.PRODUCTS_TABLE,
DBProductsTableConstants.PRODUCTS_ID_COL +
" = ?",
whereargs
);
// if originally not in a transaction then as one was started
// complete and end the transaction
if(!intransaction) {
db.setTransactionSuccessful();
db.endTransaction();
}
}

Query does not work with ORDER BY with two `in` clause in Room Persistence lib

I am implementing filter in an app where i need to use in clause
in sqlite query to fetch data but following does not work:\
#Query("SELECT * FROM NewsItem WHERE category in (:selectedCategories) and publisher in (:selectedPublishers) ORDER BY timestamp DESC LIMIT 20 OFFSET :offset")
Single<List<NewsItem>> getFilteredAscNewsList(int offset, String[] selectedCategories, String[] selectedPublishers);
Snipped that uses above method:
newsDao.getFilteredAscNewsList(offset, selectedCategories, selectedPublishers).subscribe(subscriber);
But when i remove one of the two in clause or ORDER BY clause like following, it works with out any problem:
When I remove ORDER BY clause like following, query works.
#Query("SELECT * FROM NewsItem WHERE category in (:selectedCategories) and publisher in (:selectedPublishers)")
Single<List<NewsItem>> getFilteredAscNewsList(/*int offset, */String[] selectedCategories, String[] selectedPublishers);
Snipped that uses above method:
newsDao.getFilteredAscNewsList(/*offset, */selectedCategories, selectedPublishers).subscribe(subscriber);
When i remove one of two in clause but keep ORDER BY clause, it still works.
#Query("SELECT * FROM NewsItem WHERE category in (:selectedCategories) ORDER BY timestamp DESC LIMIT 20 OFFSET :offset")
Single<List<NewsItem>> getFilteredAscNewsList(int offset, String[] selectedCategories/*, String[] selectedPublishers*/);
Snipped that uses above method:
newsDao.getFilteredAscNewsList(offset, selectedCategories/*, selectedPublishers*/).subscribe(subscriber);
Am i missing something here or there is a bug in Room lib.
I have checked it with version 1.0.0-alpha9 and 1.0.0-alpha9-1, both don't work.
does Anybody have any idea?

How To Use SQLite COUNT in Android to return number of rows

I want to write a query that add up all the rows that have the string value of "left" in column named DIRECTION. Next I want to return this sum.
In my code snip-it below assume data and data base are established.
Here is the prototype:
public int getSumLeft() {
String selectQuery = "SELECT COUNT( "+TableData.TableInfo.DIRECTION+" ) WHERE "+TableData.TableInfo.DIRECTION+" = left";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
cursor.moveToFirst();
int sum = cursor.getInt(0);
cursor.close();
return sum;
}
I've tried several queries and this one seems to be the closes to what I need. I think the problem is with statement 'int sum = cursor.getInt(0);'
I think the zero parameter is overriding the results. When I remove the zero the code breaks. getInt is an SQLite function that is used to access data in the database. I did not create that function. But I must use it or and another function like it.
Also, do I need to put a while loop around the query to move the cursor for a COUNT query? Doesn't the Database count for you, therefor no need for iteration?
Is there another way of counting the rows where the string value is 'left' and the sum can be returned?
Full code here:
Database:
https://github.com/Leoa/Accelerometer/tree/AccelerometerDEV/app/src/main/java/thedatabase
Implementation (see the button in onCreate function ):
https://github.com/Leoa/Accelerometer/blob/AccelerometerDEV/app/src/main/java/com/leobee/accelerometer/MainActivity.java
Thanks for looking into this.
I think the zero parameter is overriding the results
I have no idea what you think that this means.
When I remove the zero the code breaks
That is because getInt() needs to know the column of the Cursor to retrieve.
You are also crashing at runtime, as your SQL is invalid. Your SQL statement amounts to:
SELECT COUNT(foo) WHERE foo = left
(where foo is whatever TableData.TableInfo.DIRECTION in Java refers to)
Not only does your SQL statement lack a table to query against, but if left is supposed to be the value of a string column, you need to quote it. You will wind up with something like:
SELECT COUNT(foo) FROM tablename WHERE foo = 'left'
do I need to put a while loop around the query to move the cursor for a COUNT query?
No.
Is there another way of counting the rows where the string value is 'left' and the sum can be returned?
Not really, other than the fix that I outline above.
I think the problem is you need to add quotes on the 'left'
String selectQuery = "SELECT COUNT( "+TableData.TableInfo.DIRECTION+" ) WHERE "+TableData.TableInfo.DIRECTION+" = 'left'"

Retrieve Last 'N' records from SQLite via ContentProvider

I need to retrieve last 25 rows from a table using ContentProvider. I have searched and came up with a solution which works fine on simple SQL Database.
SELECT * FROM( SELECT * FROM [Customers] ORDER BY CustomerID DESC limit 25)ORDER BY CustomerID ASC;
But as i said , m using ContentProvider, and in my onCreateLoader the procedure is like this :
CursorLoader(getActivity(), uri, projection, selection, null, sortOrder);
in which the bold portion of the above query i.e. FROM can only be a Uri.
And if i use
( SELECT * FROM [Customers] ORDER BY CustomerID DESC limit 25) as a Uri then Exception says that is not a valid Uri.
Is there any other way to achieve my objective or any modification in this query. Thanx.
Tell the content provider to return rows sorted by CustomerID DESC.
Once you have that cursor, just take the first 25 rows out of it, and sort them again, manually. (In this case, just reverse the order of the rows.)
If you still need to have the data in a cursor afterwards, store the values in your own MatrixCursor.

SQLite query with multiple choice

Hi i'm will be wanna get rows than contains one of arguments of my string.
I have string as: "One,Two,Three,". I wanna get cursor with all data from table, that have element from this string ( For example, my find String is my string "rock,is,dead" and i wanna get rows that field contains: "rock".
Please help me, thanks
SELECT * FROM Your_Table WHERE 'rock,is,dead' LIKE '%' || String_Column || '%'
For more efficient lookups, you need to split up your search string so that the words can be checked individually, which allows the database to use index lookups (if you have an index):
SELECT * FROM Your_Table WHERE String_Column IN ('rock', 'is', 'dead')
(Please note that plain comparisons, unlike LIKE, are case sensitive, unless you use a different collation.)

Categories

Resources