Using Max in ORMLITE? - android

I need to use max in ORMLITE to retrieve the latest record in ormlite table
Similar to select max(MODIFIED)FROM TABLE;
I want to get a single record that has maximum value for datemodified having where userid is id; for particular userid i want to find record with maximum value for date ie to find the latest entry of particular user

Can't quite tell but I assume you want to know how to query for some object whose MODIFIED field is equal to the max value using ORMLite. I'm not sure you can do this in SQL without two queries. I think you are going to have to do a raw query to get the max and then do another query.
I added queryRawValue(...) to ORMLite in version 4.42 so you can do:
long max = fooDao.queryRawValue(
"select max(modified) from foo where userid = ?", id);
// now perform a second query to get the max row
Foo foo = fooDao.queryBuilder().where().eq("modified", max).queryForFirst();
By hand you would do:
GenericRawResults<String[]> rawResults =
fooDao.queryRaw("select max(modified) from foo where userid = ?", id);
// there should be one result
long max = Long.parseLong(rawResults.getResults().get(0)[0]);
// now perform a second query to get the max row
Foo foo = fooDao.queryBuilder().where().eq("modified", max).queryForFirst();

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();
}
}

Get random record in SQLite

I fetch records in my SQLite database like this.
spellId = extra.getString("spellId");
DBase db = new DBase(this);
db.open();
String[] data = db.getRecord(Integer.parseInt(spellId));
db.close();
Can I get random data like this without using raw queries and cursor?
try like this:
db.rawQuery("SELECT * FROM mainTable ORDER BY RANDOM() LIMIT 1", null);
You can use Random#nextInt() like
String[] data = db.getRecord(new Random().nextInt(num));
where num falls in the range of your record IDs. You would need to adapt this solution in case your Ids are fragmented and do not form a consecutive range.
One of the ways to do that would be to first create a query to fetch all the primary keys and store the values in a set somewhere. Then pick a random key by generating an index using Random.
String[] data = db.getRecord(IDSet.get(new Random().nextInt(IDSet.size())));
Check out the docs for more information.
Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified value (exclusive), drawn from this random number generator's sequence.
If you're considering a DB query solution
A better alternative to using ORDER BY RANDOM() (which is known to not scale well as the number of rows in your table grows) is to let SQLite return a random row using an OFFSET.
First save the total number of rows num somewhere.
SELECT COUNT(*) AS num FROM spells;
Then choose a random number rnum between (0, num) using Random and use the query
SELECT * FROM spells LIMIT 1 OFFSET rnum;

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'"

Binding NULL arguments with queryWithFactory

I'm writing an Android app that needs to write to the SQLite database. Currently it's using rawQueryWithString to build the update query, and I'm using ? placeholders in my query combined with the selectionArgs argument to pass in the actual values.
However, sometimes I actually want to update my column (of type Date) to NULL, but if I pass in null in my selectionArgs then I get this error:
IllegalArgumentException: the bind value at index 1 is null
I can't really see how I'm supposed to make it work when the value is actually null. I guess I could pass in an empty string, which in the case of a Date column like this might just work, but suppose it was a string column and I actually did want to mean NULL in contrast to the empty string (or are they considered equivalent in SQLite?)
Here's the code:
String timestampStr = null; // Obviously not really set like this
SQLiteDatabase d = getWritableDatabase();
DBCursor c = (DBCursor) d.rawQueryWithFactory(
new DBCursor.Factory(),
"Update subject set schedulingTimestamp = ? where identifier = ?",
new String[] { timestampStr, subjId.toString() },
null);
d.close();
The column was added with the following query, so I presume it's a nullable column since I didn't specify otherwise:
ALTER TABLE subject ADD schedulingTimestamp DATE;
Wildcards are not meant to be used for inserting/updating values in SQL, AFAIK. In Android, you can use ContentValues instead in conjunction with the update() method, instead of trying to shoehorn it in the raw query method.

Getting the MAX date in SQLite

Im using SQLite as data storage for my Android App. I have created one table with column of type datetime. When I do the insert of records or selects statements I use the format dd.MM.yyyy (10.08.2012) for dates.
Now, I have issue with getting the row with the latest / MAX date. I have tried using the MAX(date_column) statement, but it returns wrong date back. I have following dates in my table and it returns 31.07.2012 in stead of 04.08.2012. Anyone know what I'm doing wrong?
04.08.2012
03.08.2012
02.08.2012
01.08.2012
31.07.2012
30.07.2012
Here is part of the code:
String selection = "measurementDate = (SELECT MAX(measurementDate) FROM Measurements)";
Cursor cursor = database.query("Measurements", allColumns, selection, null, null, null, null);
cursor.moveToFirst();
...
Could it be because of 31?
Try to see if this helps:
SELECT measurementDate FROM Measurements ORDER BY measurementDate desc LIMIT 1
It's probably due to the comparison being made on the stored string rather than treating it as a date.
You could store the string in differently, YYYYMMDD, which should compare them correctly or, if you can modify the table, store the dates as milliseconds and format back to a string when needed.
This might help

Categories

Resources