【问题标题】:SQLite add column, keep dataSQLite 添加列,保留数据
【发布时间】:2011-10-01 18:26:49
【问题描述】:

我有一个包含 4 列的数据库:

@Override
public void onCreate(SQLiteDatabase database) {
        database.execSQL("CREATE TABLE " + DATABASENAME + " (name TEXT, latitude REAL, longitude REAL, country TEXT);");
}

现在我想添加另一列,但保留数据库中已有的行。我该怎么做?我找不到任何有用的参考资料。

@Override
public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
    //What to do here?
}

编辑

我已将 onCreate 修改为:

@Override
public void onCreate(SQLiteDatabase database) {
    database.execSQL("CREATE TABLE " + DATABASENAME + " (name TEXT, latitude REAL, longitude REAL, country TEXT, code TEXT);");
}

和我的 onUpgrade 到:

@Override
public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
    Logger.log("Updating userstations database from " + arg1 + " to " + arg2 + ".");
    if (arg1 == 2 && arg2 == 3) {
        db.execSQL("ALTER TABLE " + DATABASENAME + " ADD COLUMN code TEXT;");
    } else if(arg1 == 1){
        db.execSQL("DROP TABLE IF EXISTS " + DATABASENAME);
    }
    onCreate(db);
}

但是,现在我得到了这个堆栈跟踪:

10-01 21:24:19.581: ERROR/Database(21434): Failure 1 (table userstations already exists) on 0x19c378 when preparing 'CREATE TABLE userstations (name TEXT, latitude REAL, longitude REAL, country TEXT, code TEXT);'.
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434): Couldn't open userstations for writing (will try read-only):
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434): android.database.sqlite.SQLiteException: table userstations already exists: CREATE TABLE userstations (name TEXT, latitude REAL, longitude REAL, country TEXT, code TEXT);
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.database.sqlite.SQLiteDatabase.native_execSQL(Native Method)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1763)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.databases.UserStationsOpenHelper.onCreate(UserStationsOpenHelper.java:25)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.databases.UserStationsOpenHelper.onUpgrade(UserStationsOpenHelper.java:36)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:132)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:187)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.databases.UserStationsOpenHelper.getStations(UserStationsOpenHelper.java:43)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.Data.readUserStations(Data.java:369)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.Data.getUserStations(Data.java:210)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.stationstab.StationsActivity.newStations(StationsActivity.java:190)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.stationstab.StationsActivity.refresh(StationsActivity.java:129)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.stationstab.StationsActivity.onCreate(StationsActivity.java:108)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1722)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread.startActivityNow(ActivityThread.java:1598)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.LocalActivityManager.moveToState(LocalActivityManager.java:127)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.LocalActivityManager.startActivity(LocalActivityManager.java:339)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.stationstab.StationsActivityGroup.onCreate(StationsActivityGroup.java:38)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1722)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread.startActivityNow(ActivityThread.java:1598)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.LocalActivityManager.moveToState(LocalActivityManager.java:127)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.LocalActivityManager.startActivity(LocalActivityManager.java:339)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.widget.TabHost$IntentContentStrategy.getContentView(TabHost.java:654)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.widget.TabHost.setCurrentTab(TabHost.java:326)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.widget.TabHost.addTab(TabHost.java:216)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.TreinVerkeer.setupTab(TreinVerkeer.java:131)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.TreinVerkeer.initTabs(TreinVerkeer.java:108)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.myapp.myapp.TreinVerkeer.onCreate(TreinVerkeer.java:62)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1722)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1784)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread.access$1500(ActivityThread.java:123)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:939)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.os.Looper.loop(Looper.java:123)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at android.app.ActivityThread.main(ActivityThread.java:3835)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at java.lang.reflect.Method.invokeNative(Native Method)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at java.lang.reflect.Method.invoke(Method.java:507)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
10-01 21:24:19.611: ERROR/SQLiteOpenHelper(21434):     at dalvik.system.NativeStart.main(Native Method)
10-01 21:24:19.621: DEBUG/AndroidRuntime(21434): Shutting down VM
10-01 21:24:19.621: WARN/dalvikvm(21434): threadid=1: thread exiting with uncaught exception (group=0x4018a560)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434): FATAL EXCEPTION: main
10-01 21:24:19.641: ERROR/AndroidRuntime(21434): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.myapp/com.myapp.myapp.TreinVerkeer}: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.myapp/com.myapp.myapp.stationstab.StationsActivityGroup}: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.myapp/com.myapp.myapp.stationstab.StationsActivity}: android.database.sqlite.SQLiteException: Can't upgrade read-only database from version 2 to 3: /data/data/com.myapp.myapp/databases/userstations
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1768)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1784)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.access$1500(ActivityThread.java:123)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:939)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.os.Looper.loop(Looper.java:123)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.main(ActivityThread.java:3835)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at java.lang.reflect.Method.invokeNative(Native Method)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at java.lang.reflect.Method.invoke(Method.java:507)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at dalvik.system.NativeStart.main(Native Method)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434): Caused by: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.myapp/com.myapp.myapp.stationstab.StationsActivityGroup}: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.myapp/com.myapp.myapp.stationstab.StationsActivity}: android.database.sqlite.SQLiteException: Can't upgrade read-only database from version 2 to 3: /data/data/com.myapp.myapp/databases/userstations
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1768)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.startActivityNow(ActivityThread.java:1598)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.LocalActivityManager.moveToState(LocalActivityManager.java:127)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.LocalActivityManager.startActivity(LocalActivityManager.java:339)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.widget.TabHost$IntentContentStrategy.getContentView(TabHost.java:654)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.widget.TabHost.setCurrentTab(TabHost.java:326)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.widget.TabHost.addTab(TabHost.java:216)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at com.myapp.myapp.TreinVerkeer.setupTab(TreinVerkeer.java:131)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at com.myapp.myapp.TreinVerkeer.initTabs(TreinVerkeer.java:108)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at com.myapp.myapp.TreinVerkeer.onCreate(TreinVerkeer.java:62)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1722)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     ... 11 more
10-01 21:24:19.641: ERROR/AndroidRuntime(21434): Caused by: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.myapp/com.myapp.myapp.stationstab.StationsActivity}: android.database.sqlite.SQLiteException: Can't upgrade read-only database from version 2 to 3: /data/data/com.myapp.myapp/databases/userstations
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1768)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.startActivityNow(ActivityThread.java:1598)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.LocalActivityManager.moveToState(LocalActivityManager.java:127)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.LocalActivityManager.startActivity(LocalActivityManager.java:339)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at com.myapp.myapp.stationstab.StationsActivityGroup.onCreate(StationsActivityGroup.java:38)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1722)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     ... 22 more
10-01 21:24:19.641: ERROR/AndroidRuntime(21434): Caused by: android.database.sqlite.SQLiteException: Can't upgrade read-only database from version 2 to 3: /data/data/com.myapp.myapp/databases/userstations
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:199)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at com.myapp.myapp.databases.UserStationsOpenHelper.getStations(UserStationsOpenHelper.java:43)
10-01 21:24:19.641: ERROR/AndroidRuntime(21434):     at com.myapp.myapp

【问题讨论】:

    标签: android sqlite


    【解决方案1】:

    请参阅this 页面,了解在表上创建新列的语法。基本上是:

    ALTER TABLE mytable ADD COLUMN mycolumn TEXT
    

    在您的 onUpgrade 方法中,它看起来像这样:

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        String upgradeQuery = "ALTER TABLE mytable ADD COLUMN mycolumn TEXT";
        if (oldVersion == 1 && newVersion == 2)
             db.execSQL(upgradeQuery);
    }
    

    【讨论】:

    • 为什么之后还要调用 onCreate()?升级意味着 onCreate() 已经被调用(意味着表/数据库已经创建)。异常中详细说明了问题(甚至到行号 - onCreate() 中的第 25 行)。您正在尝试创建一个已经存在的表。
    • 啊,好的。但是我有从版本 1 更新后删除数据库的行,我需要调用 onCreate 吗?
    • 否 - 不要调用 onCreate(),去掉 "else if (oldVersion == 1) {... } 代码。现在如果旧版本是 2,新版本是 3,然后它会更新你的表名。而且你似乎对数据库名和表名感到困惑。你说的是创建一个名为 DATABASENAME 的表。你不是在创建数据库,而是在数据库中创建一个表。
    【解决方案2】:

    更好的方法

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
            case 1:
                db.execSQL(SQL_MY_TABLE);
    
    
            case 2:
                db.execSQL("ALTER TABLE myTable ADD COLUMN myNewColumn TEXT");
    
        }
    }
    

    假设在案例 1 中,您升级到 db 版本 2。您创建了一个新表,但忘记了您将在案例 2 中看到的 myNewColumn。如果您将 db 版本更改为 3,案例 2 将得到如果从 2 升级到 3,则运行。

    【讨论】:

    • 在版本 3 中不是 case 2nd 查询,在 case3 中是冗余查询。
    • 这在 onUpgrade 上显示旧版本何时是版本 2,因此新版本将是版本 3,它将运行代码。此代码正确且有效。
    • 我在这段代码中看到的唯一问题是您应该从 switch 语句中删除所有中断(并确保所有情况都按升序排列),以便也执行 oldVersion 情况下的所有情况.
    • 更新了我的答案.. 谢谢@TheIT
    • 不需要从 case 1 中删除 break,这是 onCreate 方法的工作。 onUpgrade 只会在数据库在新版本之前存在时才会被调用,因此执行类似于此的查询:"CREATE TABLE IF EXISTS ..." 将不起作用。
    【解决方案3】:

    我没有使用过 android,但 sqlite 提供了“alter table”,就像大多数 SQL 实现一样:

    SQLite alter table

    【讨论】:

    • ALTER TABLE MyTable ADD COLUMN MyNewCol TEXT;
    【解决方案4】:

    向数据库添加新列的正确方法(例如在版本 2 中)是:

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion < 2) {
             db.execSQL("ALTER TABLE mytable ADD COLUMN mycolumn TEXT");
        }
    }
    

    它涵盖了所有缺陷,包括所选答案的主要问题:如果用户从版本 1 升级到版本 3,他们将完全错过升级查询!这些用户将处于尴尬的境地,他们缺少一些中间更新并且没有预期的 sql 架构。

    另外不要忘记更改添加新列的创建语句。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-01
      • 2013-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-13
      • 2010-09-30
      相关资源
      最近更新 更多