SQLite MAX on Android - android

I have a database which has a table recording when a vehicle was last fuelled up. It has a date column of type integer.
I need to be able to determine which was the last fuel up date. I am using:
Cursor cursor = db.query(FUEL_USE_TABLE_NAME, LAST_FUEL_UP_DATE_CLAUSE,
REGISTRATION_NO_COLUMN + "=? ", new String[]{registrationNumber},
null, null, null);
where LAST_FUEL_UP_DATE_CLAUSE is MAX(date_time), where date_time is the name of the column in question.
This works fine when there are entries in the table, but whilst writing a unit test I expected the cursor's row count to be zero when there were no entries in the table, but instead I get a single row with value zero. I.e. the maximum value in the date_time column is zero, when in fact there are no values.
I'm happy to code around this (use 0 to signify no records instead of -1), but would like to know if this is expected behaviour, or am I doing something wrong.

It seems it is expected behavior.
See SQLite documentation here
Specifically it says:
max(X)
The max() aggregate function returns the maximum value of all values
in the group. The maximum value is the value that would be returned
last in an ORDER BY on the same column. Aggregate max() returns NULL
if and only if there are no non-NULL values in the group.

Related

Greater than operator (>) not giving correct result from database

I've text type column named 'amountDesc' having some values in it. I want to get all values which have values greater than 100. I wrote a query but it's not giving the correct result.
Database as you can see as under.
i've tried this code:
String query = "SELECT amountDesc FROM increment WHERE amountDesc > 100";
Cursor rawQuery = getReadableDatabase().rawQuery(query, null);
if (rawQuery.moveToFirst()) {
while (!rawQuery.isAfterLast()) {
String value = rawQuery.getString(rawQuery.getColumnIndex("amountDesc"));
rawQuery.moveToNext();
Log.d("tvlateamoutn1", value);
}
}
and getting these values on Logcat:
500 50 200 50
as you can see its not correct values as I required > 100 values. I know its question of for most beginners level but I stuck in it. Kindly resolve.
I've text type column named 'amountDesc' having some values in it.
So in your table definition you have amountDesc TEXT or something equivalent?
From the documentation:
A column with TEXT affinity stores all data using storage classes NULL, TEXT or BLOB. If numerical data is inserted into a column with TEXT affinity it is converted into text form before being stored.
and:
If one operand has TEXT affinity and the other has no affinity, then TEXT affinity is applied to the other operand.
Since the column has text affinity, the other operand is being converted from the integer 100 to the string '100'. The string '50' is greater than the string '100' because '5' is greater than '1'. Thus, your query is returning exactly what you're asking it to return. You're just asking it something different from what you think you are.
If you want to treat the values in that column as integers and compare them accordingly, use INTEGER not TEXT when creating the table. A poor workaround for not picking the correct affinity for the data stored in the column is to cast the values to the appropriate type when using them in calculations... CAST(amountDesc AS INTEGER) > 100 or something like that.
(Reading and understanding the linked documentation on datatypes and affinity is essential for using sqlite effectively.)
Can you check data type of amountDesc in schema. If declared data type is string, you can not compare with integer (100).

Limiting number of rows in SQLite

I have a situation where I just want to limit only 50 rows in a table. If user inserts a new row after that then first row (which was inserted very first) should get deleted and new row gets inserted, so that count remains same.
I know that I can have an rowid field and while inserting new record I can check if there are already 50 rows so delete the smallest rowid and then insert the new one. But just wanna know if there is any better solution so that I don't have to do 3 database operations (1. query #of rows, 2. delete minimum, 3. insert)
I know a way that works, but it's a little ugly. It relies on carefully constructed constraints and on seeding the database. For brevity, I'm using just five rows instead of 50.
create table test (
row_num integer primary key
check ((round(row_num) = row_num) and (row_num between 1 and 5)),
other_columns char(1) not null default 'x',
row_timestamp timestamp
not null unique
default current_timestamp
);
The expression round(row_num = row_num) guarantees you have integers in the row_num column. Otherwise, SQLite would let you insert 1.54 or 'wibble' in there.
The other_columns column is just a placeholder for your actual data.
insert into test (row_num, row_timestamp) values
(1, '2015-01-01 08:00:01'),
(2, '2015-01-01 08:00:02'),
(3, '2015-01-01 08:00:03'),
(4, '2015-01-01 08:00:04'),
(5, '2015-01-01 08:00:05');
The actual timestamp values don't really mean anything. Not yet, anyway. Seeding the database like this means that, from now on, you only have to execute update statements. If the table were empty to start with, you'd have to deal with different logic for inserts and updates. For example, you'd have to count rows to figure out whether to insert or to update.
create trigger update_timestamp
after update on test
for each row
begin
update test
set row_timestamp = strftime('%Y-%m-%d %H:%M:%f', 'now')
where row_num = OLD.row_num;
end;
The "update_timestamp" trigger makes SQLite maintain the timestamp with fractions of a second (%f). Might depend on whether the underlying OS supports fractional precision.
create trigger no_deletes
after delete on test
for each row
begin
-- There might be a more elegant way to prevent deletes.
-- This way just inserts exactly what a delete statement deletes.
insert into test (row_num, other_columns, row_timestamp)
values (OLD.row_num, OLD.other_columns, OLD.row_timestamp);
end;
Now you can update data. You update your own data, which here is just the placeholder other_columns, and SQLite takes care of the rest.
update test
set other_columns = 'b'
where row_timestamp = (select min(row_timestamp) from test);
select * from test order by row_timestamp desc;
row_num other_columns row_timestamp
---------- ------------- -----------------------
1 b 2015-03-08 12:43:21.926
5 x 2015-01-01 08:00:05
4 x 2015-01-01 08:00:04
3 x 2015-01-01 08:00:03
2 x 2015-01-01 08:00:02

ContentProvider vs SQlite: Same query different result

I have a ContentProvider that uses a custom CursorFacory in debug to print out the SQL queries (for debugging).
A certain query was returning 0 rows, while I knew there were rows that should have been included. So I copied the query from my logs, replaced the bind values and ran it in sqlite3 shell on the device and got the correct result.
The Query Code
cr.query (contentUri,
Projection.columns,
FeedColumns.FEED_TYPE + "=? AND " +
FeedColumns.SUB_TYPE + "=? AND " +
ProfileUpdateFeedItem.UPDATED_FIELD + "=? AND " +
FeedColumns.IS_NOTIFIED + "=?",
new String[] {FeedType.USER, // 2
WallPostData.WallPostType.PROFILE_UPDATE, // 1
ProfileUpdateData.ProfileField.STATUS, // 0
SQLBoolean.FALSE // 0
},
FeedColumns.CREATED + " ASC");
From the logs:
07-04 12:48:51.339 4067-4314/com.redacted.android D/DATABASE﹕ QUERY: SQLiteQuery: SELECT DISTINCT id, sender, data_1, data_2, photo, feed_type, sub_type, created, expiry, updated, comment_count, comment_unread, reaction_count, reaction_unread, sender_name, sender_photo, _id FROM wall WHERE feed_type=? AND sub_type=? AND data_1=? AND is_notified=? ORDER BY created ASC LIMIT 100
On device:
Enter SQL statements terminated with a ";"
sqlite> SELECT DISTINCT id, sender, data_1, data_2, photo, feed_type, sub_type, created, expiry, updated, comment_count, comment_unread, reaction_count, reaction_unread, sender_name, sender_photo, _id FROM wall WHERE feed_type=2 AND sub_type=1 AND data_1=0 AND is_notified=0 ORDER BY created ASC LIMIT 100;
53b702b827d7482062f52b03|a7e759d78abe4bfa97045ce49a24ab57|0|Educ||2|1|1404502712279|1404761912325|1404502712279|||||Luke Skywalker|pr/e5c2c0398b267f93683c80dc5009722e|49
The ContentProvider, however, doesn't agree and cursor.getCount() returns 0.
Any ideas why this is happening?
feed_type, sub_type, and is_notified are INTEGER columns.
data_1 is a BLOB that is storing an integer for any row that would qualify for this query, but stores strings for other types of data that could go in this table.
When you run in the shell i'm surprised you get any rows. The blob data type may not convert the keyed value properly for you. Typically the database API requires a special function to set the blob value as well as retrieve it.
So the problem here was the BLOB column. It was being evaluated properly in queries (The data in the table is used in a ListView and is displayed differently depending on the contents of the data_1 and data_2 columns).
Everything in the feed category gets parsed into a member of a class hierarchy rooted at an AnstractFeedObject.
Most fields that use both data_1 and data_2 store text in both, but some fields (those who correspond to a subset of the mentioned class hierarchy) use data_1 as a type enumeration that the UI uses to interpret the value stored in data_2. For example, a 0 type means that data_2 is a picture id (construct the url and download), while type 1 means it's actual text content.
What I ended up doing was that I replaced data_1 with an integer column called type_enumeration and renamed data_2 to data_1. Now that I know BLOB can cause those kinds of issues, I'll be changin data_2 also to a TEXT column.
If at some point in the future I need to store binary data in the DB, I'll add a bin_data to the column.
Now usually in a proper normalized schema you'd use linked tables to represent such hierarchy, but in a mobile environment, you want to minimize joins so a few extra columns are cheaper in terms of performance (at least that's been my experience).

How to order sqlite table according to accending integer?

I am having some problems with ORDER BY in android sqlite.
I am using this query to reorder my listview :
Select * From tbl_name ORDER BY WithOrder asc
Where WithOrder is an Integer type column. The expected behavior I was hoping for was that sqlite will reorder the rows as ....8,9,10,11,12.... but it is reordering the list according to the first digit instead as ....10,11,8,9....
Please help me with reordering the table with ascending values of integer...I can not opt for any other column or datatype to reorder as I depend heavily on WithOrder for general calculations when the user reorders the list.
Thanks!
Parvaz Bhaskar
While numbers are sorted according to their numerical value, strings are sorted lexicographically, beginning with the first character.
In particular, the character 1 is less than the character 8, so any string beginning with 1 (such as 11) is sorted before any string beginning with 8.
Recreate your table so that the WithOrder column has type INTEGER.
Cursor c = SQLiteDatabase_OBJ.query("Table_name", null, null, null, null, null, "WithOrder ASC");
Try this
Try this ORDER BY CAST(WithOrder AS INTEGER) ASC

Using SQLite Select function

I have created a database and all works fine. But How I can get out last five rows, which one's column value is for example 1.
The select and insert function have synchronized function, so the reading and inserting doesn't happen same time. There has more than 300 hundreds rows, but I only need get cursor object last 5 rows (so I get all columns in one row) which one's column value is 1.
Thanks for any helps!
SELECT * -- list of the columns you want
FROM table
WHERE column1 = 1 -- rows with column1 = 1
ORDER BY (ordering columns) -- columns by which you want to order
LIMIT 5 -- and get the last 5
I think you want to select the first five rows which have all have a certain column with a value of one. If so, using a WHERE and LIMIT statement should help:
SELECT * FROM tbl WHERE the_column = 1 LIMIT 5
You can Use the method database.query();
as follows:
Cursor cursor = database.query(false, TABLE,new String[] {COLUMNS_TO_SELECT},"YOUR_WHERE_CLAUSE", null, null, null, null /*ORDER BY*/, "5"/*YOUR_LIMIT*/);
Have a look at Documentation

Categories

Resources