在我的SQLite数据库的表中,有一个时间戳(millisec)列。这个表非常庞大,我想要一种高效的方式来查询there timestamp列引用特定日期的所有行(在这种情况下,我想获得属于TODAY的所有enteries)。
我想出了这个,但它不起作用而且速度很慢。任何想法都会得到满足。 (不,我不能在数据库中进行任何更改,例如添加DATETIME列......我必须使用时间戳进行更改)。
public boolean isTimestampInToday(long timestamp){
//get a timestamp from today
long todayStamp = System.currentTimeMillis();
Calendar c = Calendar.getInstance();
c.setTimeInMillis(todayStamp);
c.setTimeZone(TimeZone.getDefault());
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
Date startDate = c.getTime(); //START OF DAY
c.set(Calendar.HOUR_OF_DAY, 23);
c.set(Calendar.MINUTE, 59);
c.set(Calendar.SECOND, 59);
c.set(Calendar.MILLISECOND, 999);
Date endDate = c.getTime(); //END OF DAY
Long startStamp = startDate.getTime();
Long endStamp = endDate.getTime();
if(timestamp >= startStamp && timestamp < endStamp) {
return true;
} else {
return false;
}
}
我从expirienced家伙那里得到了所有答案,但说实话,我能理解的是来自@iaune的答案。现在我在查询本身内完成了这个:
Calendar gc = new GregorianCalendar();
gc.setTimeZone(TimeZone.getDefault());
long toBeg = gc.getTimeInMillis();
toBeg -= toBeg % (24*60*60*1000);
long toEnd = toBeg + 24*60*60*1000;
Cursor cursor = mDb.rawQuery(
"SELECT * FROM "
+ DbHelper.TABLE_PA_DATA
+ " WHERE "
+ DbHelper.COLUMN_TIMESTAMP
+ " BETWEEN "
+ toBeg
+ " AND "
+ toEnd
, null);
这种方法有什么问题,还是可以进一步改进?
更新
我意识到使用INTEGER
作为时间戳列类型是错误的。我添加了一个DATETIME
列...我现在应该使用直接的SQLite查询函数。任何想法?
这是我转储桌子时得到的结果:
ID DATETIME TIMESTAMP STEPS CALORIES
1 2014-07-06 07:18:55 169629539 3 0
2 2014-07-06 07:19:10 169644509 4 0
3 2014-07-06 08:15:36 173030229 1 0
4 2014-07-06 08:16:04 173058397 4 0
5 2014-07-06 08:31:39 173993598 2 0
6 2014-07-06 08:33:20 174094714 5 0
7 2014-07-06 09:31:54 177609142 1 0
8 2014-07-06 09:42:24 178238517 1 0
9 2014-07-06 10:37:37 181551849 1 0
10 2014-07-06 11:00:31 182925950 1 0
11 2014-07-06 12:17:42 187557035 1 0
12 2014-07-06 14:14:39 194573993 2 0
好的,我设法在@laune解决方案中添加了一些内容:
Calendar cal = Calendar.getInstance();
// offset to add since we're not UTC
long offset = cal.get(Calendar.ZONE_OFFSET) +
cal.get(Calendar.DST_OFFSET);
long toBeg = cal.getTimeInMillis();
toBeg -= (toBeg + offset) % (24*60*60*1000);
long toEnd = (toBeg) + 24*60*60*1000;
使用此查询:
Cursor cursor = mDb.rawQuery(
"SELECT * FROM "
+ Database.TABLE_PA_DATA
+ " WHERE "
+ Database.COLUMN_TIMESTAMP
+ " BETWEEN "
+ toBeg
+ " AND "
+ toEnd, null
);
答案 0 :(得分:3)
使用PreparedStatement
选择您的数据,并使用包含
where
子句
date_column between ? and ?
一旦您计算了startDate
和endDate
- 您上面的方式完全没问题 - 将它们设置为PreparedStatement
中的参数并运行它。
答案 1 :(得分:2)
我会像这样使用SimpleDateFormat
和new Date(long)
,
// four digit year, two digit month, two digit day. For example, 20140706
private final DateFormat sdf = new SimpleDateFormat("yyyyMMdd");
public boolean isTimestampInToday(final long timestamp){
return sdf.format(new Date(timestamp)).equals(sdf.format(new Date()));
}
答案 2 :(得分:2)
使用“半开”方法可以更好地完成日期时间比较,其中一段时间的开始是包容性的,结尾是独占的。对于一天来说,这意味着您希望查询日期时间大于或等于当天的第一时刻,而不是一天中的第一时刻。也就是说,最后一点不包括第二天。这种方法避免了在一天的最后时刻无限分裂的问题。
java,util.Date和.Calendar类非常麻烦。避免他们。请改用Joda-Time库。它适用于Android。
时区至关重要。一天的定义取决于时区。
指定所需的时区,而不是依赖隐式默认值。使用proper time zone names,而不是3或4个字母代码。
如果你真的想使用JVM的默认时区,我建议显式调用getDefault
而不是依赖隐式默认值。这种依赖往往会导致日期工作混乱。
DateTimeZone timeZone = DateTimeZone.getDefault(); // Or DateTimeZone.forID( "Europe/Amsterdam" )
DateTime now = DateTime.now( timeZone );
DateTime todayStartOfDay = now.withTimeAtStartOfDay():
DateTime tomorrowStartOfDay = today.plusDays( 1 ).withTimeAtStartOfDay();
从那里你可以调用getMillis
得到一个long,你可以用它为你的SQL查询构造一个java.sql.Timestamp对象。
对于Half-Open方法,我们不能使用SQL命令BETWEEN
。两个比较器都包含该命令。
相反,我们需要编写两个比较器,就像这个伪代码一样。注意>=
与<
(没有等号)。
SELECT *
FROM some_table_
WHERE target >= todayStartOfDay.getMillis()
AND target < tomorrowStartOfDay.getMillis();
答案 3 :(得分:1)
简单的算术运算可以避免昂贵的字符串和格式化操作。不确定大卫是否意味着我的建议,但也许代码表达得更清楚:
Calendar gc = new GregorianCalendar();
long toBeg = gc.getTimeInMillis();
toBeg -= toBeg % (24*60*60*1000);
long toEnd = toBeg + 24*60*60*1000;
long now = gc.getTimeInMillis();
if( toBeg <= now && now < toEnd ){
System.out.println( "today" );
}
根据if,可以使用预先计算的toBeg和toEnd值来运行查询。 (确切地说,请注意使用<=
和<
。)
答案 4 :(得分:1)
我会这样做
boolean isTimestampInToday(long timestamp) {
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c2.setTimeInMillis(timestamp);
return c1.get(Calendar.DATE) == c2.get(Calendar.DATE);
}
答案 5 :(得分:0)
DateUtils.isToday(timeStampInMilli)
您可以使用此方法检查时间戳记是否为今天。