I am working on an application where i practice working with Room Persistence Library.
I have a class called expence based on which is the database with fields int amount and Date dob and other.
My goal is to use query below to return the sum of all entries between these dates.
#Query("SELECT SUM(amount) FROM expence WHERE dob BETWEEN date( :start ) AND date( :end ) ")
int newallexpensesFromTo(String start,String end);//
But at the curent state the query doesnt return anything and the textview i want to display the result = 0;
I have used guidance from http://androidkt.com/datetime-datatype-sqlite-using-room/ - to be able to convert from strings to Date and revese.
I have checked maybe its due to different format of the stored date and its the same the one stored in database and the one passed to query.
This is where i try to get the sum of the entries
value2 =db.expenceDao().newallexpensesFromTo(firstdayofmonth,lastdayofmonth);
I have a similar query where without the dates and it returns all entries.
When i add an expense i use DatePicker to add the Date to the dob in database.
Expected result is to receive the sum of entries added between these dates when the query receives the strings with the dates.
Try converting and inserting your startDate and endDate as a long timestamp value into your expense table instead of storing it in some another format.
Now while querying the data, use the timestamp values of your query dates.
For e.g., if you want SUM(amount) between 01-05-2019 and 31-05-2019 than your query would be like:
#Query("SELECT SUM(amount) FROM expense WHERE dob BETWEEN :startDate AND :endDate")
int newAllExpensesFromTo(Long startDate,Long endDate);
Where your startDate and endDate values will be something like 1556668800 & 1559260800 respectively.
For now you can use type converters.
As described in docs for Date your converter in Java should looks like below
public class Converters {
#TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
#TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
To apply them, add below code above the class inheriting from RoomDatabase()
#TypeConverters({Converters.class})
Finally you will be able to use it like that
#Query("SELECT SUM(amount) FROM expence WHERE dob BETWEEN :from AND :to")
int newallexpensesFromTo(from: Date, to: Date)
Related
I have this Entry data class
#Entity(tableName = "entry")
#Typeconverters(DateConverter::class)
data class Entry(
#PrimaryKey(autoGenerate = false)
var id : String,
var username : String,
var type : String,
var description : String,
var category : String,
var amount : Double,
var date : String,
var lastUpdate : String,
var isDeleted : Boolean)
}
The date field contains a string that represents a date in the "yyyy-MM-dd" format, while the lastUpdate contains a string that represents a date in the "yyyy-MM-dd hh:mm:ss" format. If i store those variables as strings i cannot do SQL comparisons on them since Room does not support SQL's DATE() and DATETIME() datatype and thus queries like this:
#Query(SELECT * FROM entry WHERE date >= :fromDate AND date <= :untilDate)
Will not work properly. Is there any way to fix this?
Well, I see 3 options.
Since your date string is formatted in a nice hierarchical way (year, month, day), you should actually be able to use its natural String sort.
If you need real date sort within a SQL query, you'll have to save your date as real date-field or integer field (Unix epoch timestamp)
If it is okay to sort the date after fetching it from the DB or before storing it in the DB, make yourself familiar with TypeAdapter in Room. It's a simple conversion class where you can convert from String to DateTime and back.
To answer your second question on why such "common" data type is not supported out-of-the box, I can recommend this medium article:
SQLite is a loosely typed database system and stores all values as one
of: NULL, INTEGER, TEXT, REAL or BLOB. You’ll notice that there is no
special date or time type like you may find in other database systems.
Instead they provides the following documentation on how to store
date/time values: SQLite does not have a storage class set aside for
storing dates and/or times. Instead, the built-in Date And Time
Functions of SQLite are capable of storing dates and times as TEXT,
REAL, or INTEGER values
If you think about it further, the question arises: What is a common data type and where does "common" end. Of course, they could provide some TypeConverters, but on the other hand it's a few lines of code for each data type.
Here is an example for a TypeConverter from Date to String and back:
public class Converters {
#TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
#TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
I have overhauled my room database from String Dates to Date which is stored as a Long using type converters. I am unable to filter my results for specific times like -7 days or 3 months etc. Here is my configuration:
QUERY
SELECT *
FROM moodBeforetable
WHERE moodBeforetable.workoutDate >= datetime('now', '-1 year')
ORDER BY moodBeforetable.workoutDate DESC
LiveData<List<WorkoutLogsAllPojo>> getAllMoodLogs();
The query works fine when the WHERE clause is commented out, however if I include that line above no data is returned.
This moodBeforetable.workoutDate variable is a DATE but its stored in the database as Long :1590705660000
Type Converter
#TypeConverter
public static Date toDate(Long timestamp){
return timestamp == null ? null : new Date(timestamp);
}
#TypeConverter
public static Long toTimestamp(Date date){
return date == null ? null : date.getTime();
}
Any assistance on the root cause of why the WHERE clause is not returning any data or the specified amount of rows would be greatly appreciated.
Function datetime('now', '-1 year') returns timestamp as '%Y-%m-%d %H:%M:%S', for example 2019-05-28 23:29:42 (docs). To get the number of milliseconds since epoch, use 1000 * strftime('%s', datetime('now', '-1 year')) instead.
I want to query my db with a date comparision with current Date.
Here is my query :
#Query("SELECT * FROM User INNER JOIN AutoLogin ON User.user_Id = AutoLogin.user_Id AND AutoLogin.token_Id = :autoLoginToken AND Autologin.expiration_date > date('now')")
Single<User> getUserByAutoLogin(String autoLoginToken);
Here is my AutoLogin class :
public class AutoLogin implements Parcelable {
#NonNull
#PrimaryKey
#ColumnInfo(name = "token_Id")
private String tokenId;
#NonNull
#TypeConverters(DataTypeConverter.class)
#ColumnInfo(name = "expiration_date")
private Date expirationDate;
#NonNull
#ColumnInfo(name = "user_Id")
private Long userId;
My converters :
#androidx.room.TypeConverter
public static Date toDate(Long value) {
return value == null ? null : new Date(value);
}
#androidx.room.TypeConverter
public static Long toLong(Date value) {
return value == null ? null : value.getTime();
}
The query doesn't work and retrieve no result. I feel this is a problem with the date part of the it. Anybody see what's my error ?
Thanks.
There are 5 functions which SQLite provides:
date(...) returns just the date.
time(...) returns just the time.
datetime(...) returns both the date and time.
julianday(...) returns the Julian Day.
strftime(...) returns a value formatted with your given format
string. The first four can be thought of as variations of strftime
with a pre-defined format.
For more information read the blog
https://medium.com/androiddevelopers/room-time-2b4cf9672b98
You not do same compare with Autologin.expiration_date > date('now')"
expiration_date be like 1579550175468 date('now') be like 2020-01-20
To compare same and want to do by date and no time you can use
#Query("SELECT * FROM User INNER JOIN AutoLogin ON " +
"User.user_Id = AutoLogin.user_Id " +
"AND AutoLogin.token_Id = :autoLoginToken " +
"AND date(Autologin.expiration_date / 1000,'unixepoch') > date('now')")
Other Answer say
#Query("SELECT * FROM User INNER JOIN AutoLogin ON User.user_Id = AutoLogin.user_Id AND AutoLogin.token_Id = :autoLoginToken AND Autologin.expiration_date > strftime('%s', 'now')")
not work well do compare 1579550175468 with 1579551916 it not do / 1000, if it do, it have time to second
Since you used Date.getTime() method in your TypeConverter class, the value stored in DB will be Unix Time.
long getTime() Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object.
So in your query, you must compare expiration_date column value with current Unix time. Based on SQLite official website, you can get current Unix time by strftime('%s', 'now') expression. Your query should be like below:
#Query("SELECT * FROM User INNER JOIN AutoLogin ON User.user_Id = AutoLogin.user_Id AND AutoLogin.token_Id = :autoLoginToken AND Autologin.expiration_date > (strftime('%s', 'now') * 1000)")
Single<User> getUserByAutoLogin(String autoLoginToken);
I'm working on an activity which have 6 radio buttons which are(1-All, 2-Today, 3-Yesterday, 4-Last Week, 5-This Month, 6-Last Month) <=These are the names of the radio buttons.
So when I check the Today's radio button and press OK button, it should show me all entries which are inserted in database in today's date, and the reset of radio buttons should give me the result as there names are, as if I check Yesterday, it should give me the data that was stored yesterday.
The problem is I don't know the exact queries for this scenario which will give me the desired data.
I'm using ROOM database and in Dao class I've to write the queries which will give me data according to radio buttons.
There is a Date and Time column which stores the current date and time when the data is store in this table, so I have to match the date of table with the date of Today, Yesterday and so on, and according to that matching the data will be filter.
This is my Entity class(Table) from where I want to get data according to date.
#Entity
public class IncomeExpense {
#PrimaryKey(autoGenerate = true)
private int id =0;
private String type;
private int amount;
private String category;
private String date;
private String time;
private String mode;
private String note;
I just want someone who can write me Dao queries & functions which will give me filter data according to radio buttons.
I have achieved this by using dates in string format. Like Standard format of dates 08-12-2015. In Dao Class I used the following query to get the record for date. note that in my entity class date was also in String format.
#Query("SELECT * from scan_data where sqlDate = date('now') ")
List<Data> getTodayRecord();
For YesterDay Record
#Query("SELECT * from scan_data where sqlDate = date('now','-1 day')")
List<Data> getYesterDayRecord();
For Last Week Record
#Query("SELECT * FROM scan_data WHERE DATE(sqlDate) >= DATE('now', 'weekday 0', '-7 days') AND DATE(sqlDate) != DATE('now') AND DATE(sqlDate) != DATE('now','-1 day')")
List<Data> getLastWeekRecord();
For Month Record
#Query("SELECT * FROM scan_data WHERE strftime('%W',sqlDate) != strftime('%W',date('now')) AND strftime('%Y',sqlDate) = strftime('%Y',date('now')) AND strftime('%m',sqlDate) = strftime('%m',date('now')) AND DATE(sqlDate) != DATE('now', 'weekday 0', '-7 days') AND DATE(sqlDate) != DATE('now') AND DATE(sqlDate) != DATE('now','-1 day')")
List<Data> getLastMonthRecord();
In the beginning, I do private String date instead of private Date date later because of this I can't do the date filter as I want. So use Date data type java.util.Date
I have a room database. I have a column for date and it's saved as string.
I used this query for sort my column :
#Query("SELECT * FROM session WHERE class_id = :classId ORDER BY session_date ASC")
List<SessionEntry> getAllSessions(int classId);
Result :
1398/11/25
1398/11/29
1398/12/5
1398/2/14
1398/4/25
1398/6/17
1398/6/30
1398/7/9
1398/9/14
but i want to sort like this :
1398/2/14
1398/4/25
1398/6/17
1398/6/30
1398/7/9
1398/9/14
1398/11/25
1398/11/29
1398/12/5
Is there any way I could order by Date as String without modifying the database structure ?
in my case it is working as I have like float type formate :
#Query("SELECT * FROM growthlogdata WHERE babyid = :childid Order By CAST(dateGrowth AS FLOAT) ASC")
List<SessionEntry> getAllSessions(int classId);
First answer, don’t store dates as strings, use a proper date datatype.
However, if I understand correctly that your SQLite database hasn’t got a date datatype, #matdev is correct: The best solution is to change the format into yyyy-mm-dd. This conforms with ISO 8601 and will sort chronologically.
I found a solution but it's not best.
This way is for when you can't change your Date Column from String to another type
List<SessionEntry> sessionEntries = mDb.sessionDao().getAllSessions(classId);
Collections.sort(sessionEntries, comparing(SessionEntry::convertStringToDate));
Session Entry :
public class SessionEntry {
.
.
.
public Date convertStringToDate() {
try {
return new SimpleDateFormat("yyyy/MM/dd").parse(getSessionDate());
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}