【问题标题】:SQL Timestamp overflows in Android JavaAndroid Java 中的 SQL 时间戳溢出
【发布时间】:2021-06-02 23:48:21
【问题描述】:

我一直在编写一个 Android 应用程序并遇到了一个奇怪的问题。我有一个使用内部 Android SQL 数据库设置的数据库,并且有一个定义为时间戳的字段。问题是检索值似乎无法在不导致 long 数据类型溢出的情况下完成,而且据我所知,没有从 SQL 数据库中获取 biginteger 或其他任何有用的信息。

关于从 SQL 数据库中检索时间戳值,我是否遗漏了什么?

编辑:由于缺少实际的 TIMESTAMP 类型,显然该值可能会在 SQLite 数据库内部溢出。

【问题讨论】:

  • Sqlite 没有“时间戳”类型。您如何将时间存储在数据库中?
  • 安卓版可以。该字段定义为 TIMESTAMP 类型:mydatabase.execSQL("CREATE TABLE IF NOT EXISTS HealthyUnhealthy(ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, activity VARCHAR NOT NULL, category VARCHAR, time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, unhealthy INTEGER NOT NULL DEFAULT 0) ;");
  • 您可以使用几乎任何您想要的作为 sqlite 中的列类型。 fhtrdsdvhu 有效。详情请见sqlite.org/datatype3.html
  • 有趣。虽然改变不了问题。由于 getLong 不够并且溢出,因此无法检索时间戳。当然,除非它由于定义方式的其他问题而在内部溢出。
  • 看来数据库实际上是在内部溢出。我尝试将类型更改为 INT、REAL 甚至 BLOB,但它们都溢出了。 BLOB 溢出根本没有实际意义。

标签: java android sqlite


【解决方案1】:

我相信您的问题不在于 SQLite,而在于您对提取数据的处理。

这可能源于您使用 DEFAULT CURRENT_TIMESTAMP 并没有意识到这会将数据保存为字符串(SQLite 中的列类型 TEXT),格式为 YYYY-MM-DD hh:mm:ss,而不是很长,然后尝试将其转换为long from a string (参见演示结果中的at a.a.so67813806javatimestampoverflow.MainActivity.onCreate(MainActivity.java:43))

因此修复可能有两个方面:-

  1. 以相同的格式存储数据(或在提取时适当处理,前者是更理想的解决方案)
  2. 使用适当的get????从光标检索数据时的方法。即获得长期使用 getLong 等

就溢出而言,有很多纬度。例如,以 Java 的 System.currentTimeMillis() 为例,这会将日期和时间返回到 1000 秒。

  • 在日期/时间 2021-06-03 12:33:35.579 这样的值将是 1622687615571。

将其乘以 100000 得到 1,622,687,615,574,000,000,仍然可以作为 long 存储和检索(仍然在 long 的 9,223,372,036,854,775,807 限制内)。

演示

作为能力证明,请考虑以下基于您的 sn-p 的演示/应用程序,显示用于创建表的 SQL。

首先是数据库助手DBHelper:-

class DBHelper extends SQLiteOpenHelper {

    public static final String DBNAME = "mydb";
    public static final int DBVERSION = 1;
    private SQLiteDatabase db;

    public DBHelper(@Nullable Context context) {
        super(context, DBNAME, null, DBVERSION);
        db = this.getWritableDatabase();

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS HealthyUnhealthy(" +
                "ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
                "activity VARCHAR NOT NULL, " +
                "category VARCHAR, " +
                "time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
                "unhealthy INTEGER NOT NULL DEFAULT 0);"
        );
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {

    }

    public long insert(String activity, String category, Long timeStamp, boolean unhealthy) {
        ContentValues cv = new ContentValues();
        cv.put("activity",activity);
        cv.put("category",category);
        if (timeStamp != null) {
            cv.put("time", timeStamp);
        }
        cv.put("unhealthy",unhealthy);
        return db.insert("HealthyUnhealthy",null,cv);
    }

    public Cursor getAllData() {
        String[] columns = new String[]{
                "id","activity","category","time","unhealthy","typeOf(time)"
        };
        return db.query("HealthyUnhealthy",columns,null,null,null,null,null);
    }
}
  • 最受关注的是 insertgetAllData 方法
  • insert 通过默认值(如果 Long 传递为 null)或任何 long 传递来满足插入。
  • getAlldata 返回一个包含所有列的 Cursor 以及一个显示时间存储类型的附加列(请注意,这是用于存储数据的类型,而不是类型关联,也就是用于定义列的列类型)

Second 是一个活动,MainActivity,它使用数据库和前面描述的方法。

  • 插入了一些行,第一行使用 Java 的 System.currentTimeInMillis(),第二行使用 CURRENT_TIMESTAMP,另外 6 行使用系统时间乘以 10。

  • 所有数据都被提取到一个光标中。

  • 光标被转储

  • 遍历游标并提取列。使用各种Cursor get 多次提取时间列????方法。

:-

public class MainActivity extends AppCompatActivity {

    DBHelper db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = new DBHelper(this);
        db.insert("Activity1","Category1",System.currentTimeMillis(),false);
        db.insert("Activity2","Category2",null,false); // use default
        db.insert("Activity3","Category3",System.currentTimeMillis() * 10,false);
        db.insert("Activity4","Category4",System.currentTimeMillis() * 100,false);
        db.insert("Activity5","Category5",System.currentTimeMillis() * 1000,false);
        db.insert("Activity6","Category6",System.currentTimeMillis() * 10000,false);
        db.insert("Activity7","Category7",System.currentTimeMillis() * 100000,false);
        db.insert("Activity8","Category8",System.currentTimeMillis() * 1000000,false);


        Cursor csr = db.getAllData();
        DatabaseUtils.dumpCursor(csr);
        while (csr.moveToNext()) {
            long time =csr.getLong(csr.getColumnIndex("time"));
            Log.d(
                    "TIMEINFO",
                    "Time for Activity " + csr.getString(csr.getColumnIndex("activity")) +
                            " is (as long) " + csr.getLong(csr.getColumnIndex("time")) +
                            " as (float) " + csr.getFloat(csr.getColumnIndex("time")) +
                            " as (Double) " + csr.getDouble(csr.getColumnIndex("time")) +
                            " as (String) " + csr.getString(csr.getColumnIndex("time")) +
                            " as (Int) " + csr.getInt(csr.getColumnIndex("time"))
            );
            try {
                long x = Long.parseLong(csr.getString(csr.getColumnIndex("time")));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        csr.close();
    }
}

结果

首先将光标作为转储:-

2021-06-03 13:25:42.748 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@6131588
2021-06-03 13:25:42.749 I/System.out: 0 {
2021-06-03 13:25:42.749 I/System.out:    ID=1
2021-06-03 13:25:42.749 I/System.out:    activity=Activity1
2021-06-03 13:25:42.749 I/System.out:    category=Category1
2021-06-03 13:25:42.749 I/System.out:    time=1622690742744
2021-06-03 13:25:42.749 I/System.out:    unhealthy=0
2021-06-03 13:25:42.749 I/System.out:    typeOf(time)=integer
2021-06-03 13:25:42.749 I/System.out: }
2021-06-03 13:25:42.749 I/System.out: 1 {
2021-06-03 13:25:42.749 I/System.out:    ID=2
2021-06-03 13:25:42.750 I/System.out:    activity=Activity2
2021-06-03 13:25:42.750 I/System.out:    category=Category2
2021-06-03 13:25:42.750 I/System.out:    time=2021-06-03 03:25:42
2021-06-03 13:25:42.750 I/System.out:    unhealthy=0
2021-06-03 13:25:42.750 I/System.out:    typeOf(time)=text
2021-06-03 13:25:42.750 I/System.out: }
2021-06-03 13:25:42.750 I/System.out: 2 {
2021-06-03 13:25:42.750 I/System.out:    ID=3
2021-06-03 13:25:42.750 I/System.out:    activity=Activity3
2021-06-03 13:25:42.750 I/System.out:    category=Category3
2021-06-03 13:25:42.750 I/System.out:    time=16226907427450
2021-06-03 13:25:42.750 I/System.out:    unhealthy=0
2021-06-03 13:25:42.750 I/System.out:    typeOf(time)=integer
2021-06-03 13:25:42.750 I/System.out: }
2021-06-03 13:25:42.750 I/System.out: 3 {
2021-06-03 13:25:42.750 I/System.out:    ID=4
2021-06-03 13:25:42.750 I/System.out:    activity=Activity4
2021-06-03 13:25:42.750 I/System.out:    category=Category4
2021-06-03 13:25:42.751 I/System.out:    time=162269074274600
2021-06-03 13:25:42.751 I/System.out:    unhealthy=0
2021-06-03 13:25:42.751 I/System.out:    typeOf(time)=integer
2021-06-03 13:25:42.751 I/System.out: }
2021-06-03 13:25:42.751 I/System.out: 4 {
2021-06-03 13:25:42.751 I/System.out:    ID=5
2021-06-03 13:25:42.751 I/System.out:    activity=Activity5
2021-06-03 13:25:42.751 I/System.out:    category=Category5
2021-06-03 13:25:42.751 I/System.out:    time=1622690742746000
2021-06-03 13:25:42.751 I/System.out:    unhealthy=0
2021-06-03 13:25:42.751 I/System.out:    typeOf(time)=integer
2021-06-03 13:25:42.751 I/System.out: }
2021-06-03 13:25:42.751 I/System.out: 5 {
2021-06-03 13:25:42.751 I/System.out:    ID=6
2021-06-03 13:25:42.751 I/System.out:    activity=Activity6
2021-06-03 13:25:42.751 I/System.out:    category=Category6
2021-06-03 13:25:42.751 I/System.out:    time=16226907427470000
2021-06-03 13:25:42.751 I/System.out:    unhealthy=0
2021-06-03 13:25:42.751 I/System.out:    typeOf(time)=integer
2021-06-03 13:25:42.752 I/System.out: }
2021-06-03 13:25:42.752 I/System.out: 6 {
2021-06-03 13:25:42.752 I/System.out:    ID=7
2021-06-03 13:25:42.752 I/System.out:    activity=Activity7
2021-06-03 13:25:42.752 I/System.out:    category=Category7
2021-06-03 13:25:42.752 I/System.out:    time=162269074274700000
2021-06-03 13:25:42.752 I/System.out:    unhealthy=0
2021-06-03 13:25:42.752 I/System.out:    typeOf(time)=integer
2021-06-03 13:25:42.752 I/System.out: }
2021-06-03 13:25:42.752 I/System.out: 7 {
2021-06-03 13:25:42.752 I/System.out:    ID=8
2021-06-03 13:25:42.752 I/System.out:    activity=Activity8
2021-06-03 13:25:42.752 I/System.out:    category=Category8
2021-06-03 13:25:42.753 I/System.out:    time=1622690742748000000
2021-06-03 13:25:42.753 I/System.out:    unhealthy=0
2021-06-03 13:25:42.753 I/System.out:    typeOf(time)=integer
2021-06-03 13:25:42.753 I/System.out: }
2021-06-03 13:25:42.753 I/System.out: <<<<<
  • 注意 第二行 (Activity2) 的存储类型为 TEXT,根据 I/System.out:typeOf(time)=text,数据存储为time=2021-06-03 02:45:36。类型为 INTEGER 的所有其他行。

输出的第二部分来自游标的遍历,是:-

2021-06-03 13:25:42.753 D/TIMEINFO: Time for Activity Activity1 is (as long) 1622690742744 as (float) 1.62269076E12 as (Double) 1.622690742744E12 as (String) 1622690742744 as (Int) -806895144
2021-06-03 13:25:42.753 D/TIMEINFO: Time for Activity Activity2 is (as long) 2021 as (float) 2021.0 as (Double) 2021.0 as (String) 2021-06-03 03:25:42 as (Int) 2021
2021-06-03 13:25:42.753 W/System.err: java.lang.NumberFormatException: For input string: "2021-06-03 03:25:42"
2021-06-03 13:25:42.754 W/System.err:     at java.lang.Long.parseLong(Long.java:594)
2021-06-03 13:25:42.754 W/System.err:     at java.lang.Long.parseLong(Long.java:636)
2021-06-03 13:25:42.754 W/System.err:     at a.a.so67813806javatimestampoverflow.MainActivity.onCreate(MainActivity.java:43)
2021-06-03 13:25:42.754 W/System.err:     at android.app.Activity.performCreate(Activity.java:7136)
2021-06-03 13:25:42.754 W/System.err:     at android.app.Activity.performCreate(Activity.java:7127)
2021-06-03 13:25:42.754 W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
2021-06-03 13:25:42.754 W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
2021-06-03 13:25:42.755 W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
2021-06-03 13:25:42.755 W/System.err:     at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
2021-06-03 13:25:42.755 W/System.err:     at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
2021-06-03 13:25:42.755 W/System.err:     at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
2021-06-03 13:25:42.755 W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
2021-06-03 13:25:42.755 W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:106)
2021-06-03 13:25:42.755 W/System.err:     at android.os.Looper.loop(Looper.java:193)
2021-06-03 13:25:42.755 W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6669)
2021-06-03 13:25:42.756 W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
2021-06-03 13:25:42.756 W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
2021-06-03 13:25:42.756 W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2021-06-03 13:25:42.756 D/TIMEINFO: Time for Activity Activity3 is (as long) 16226907427450 as (float) 1.62269076E13 as (Double) 1.622690742745E13 as (String) 16226907427450 as (Int) 520983162
2021-06-03 13:25:42.757 D/TIMEINFO: Time for Activity Activity4 is (as long) 162269074274600 as (float) 1.62269082E14 as (Double) 1.622690742746E14 as (String) 162269074274600 as (Int) 914864424
2021-06-03 13:25:42.757 D/TIMEINFO: Time for Activity Activity5 is (as long) 1622690742746000 as (float) 1.62269072E15 as (Double) 1.622690742746E15 as (String) 1622690742746000 as (Int) 558709648
2021-06-03 13:25:42.757 D/TIMEINFO: Time for Activity Activity6 is (as long) 16226907427470000 as (float) 1.62269072E16 as (Double) 1.622690742747E16 as (String) 16226907427470000 as (Int) 1292139184
2021-06-03 13:25:42.757 D/TIMEINFO: Time for Activity Activity7 is (as long) 162269074274700000 as (float) 1.6226907E17 as (Double) 1.622690742747E17 as (String) 162269074274700000 as (Int) 36489952
2021-06-03 13:25:42.758 D/TIMEINFO: Time for Activity Activity8 is (as long) 1622690742748000000 as (float) 1.62269073E18 as (Double) 1.622690742748E18 as (String) 1622690742748000000 as (Int) 365899520
  • 可以看出 1622688336337000000 已被存储和检索而没有问题,这比任何精确到毫秒的时间戳都要大得多,并且可以通过游标方法 getLong()getString()getFloat() 和 @987654335 很好地处理@
  • 但是,第二行也有问题。除了 getString() 方法之外的所有方法都只得到 2021(第一个非数字就是这样,所以该方法可以合理地返回一些东西)。
  • 最重要/相关是尝试将第二行解析为长结果at a.a.so67813806javatimestamp**overflow**.MainActivity.onCreate(MainActivity.java:43)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-18
    • 2017-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 2014-06-09
    相关资源
    最近更新 更多