如何在Android SQLite数据库中检查表是否存在?

95
我有一个Android应用程序,需要检查数据库中是否已有记录,如果没有,则处理一些事情并最终将其插入,并且如果数据已经存在,则只需从数据库中读取数据。我正在使用SQLiteOpenHelper的子类来创建和获取可重写实例的SQLiteDatabase,并认为它会自动处理创建表(因为要执行此操作的代码在onCreate(...)方法中)。
但是,当表尚不存在,并且我拥有的SQLiteDatabase对象上运行的第一个方法是查询(...)调用时,我的logcat显示错误“I/Database(26434):sqlite returned: error code = 1, msg = no such table: appdata”,而appdata表确实没有被创建。
有任何想法吗?
我要么寻找一种测试表是否存在的方法(因为如果没有表,数据肯定不在其中,直到写入它之前我不需要读取它,这似乎可以正确创建表),或者一种确保它得到创建的方法,并且在第一次调用query(...)之前为空。
编辑: 这是在下面两个答案发布后发布的: 我认为我可能找到了问题所在。出于某种原因,我决定每个表都应该创建一个不同的SQLiteOpenHelper,尽管两者都访问相同的数据库文件。我认为重构该代码以仅使用一个OpenHelper,并在其onCreate中创建两个表可能效果更好...
13个回答

132

试一下这个:

public boolean isTableExists(String tableName, boolean openDb) {
    if(openDb) {
        if(mDatabase == null || !mDatabase.isOpen()) {
            mDatabase = getReadableDatabase();
        }

        if(!mDatabase.isReadOnly()) {
            mDatabase.close();
            mDatabase = getReadableDatabase();
        }
    }

    String query = "select DISTINCT tbl_name from sqlite_master where tbl_name = '"+tableName+"'";
    try (Cursor cursor = mDatabase.rawQuery(query, null)) {
        if(cursor!=null) {
            if(cursor.getCount()>0) {
                return true;
            }
        }
        return false;
    }
}

11
不要忘记 cursor.close(); - Tyler
1
由于未知原因,表名区分大小写。我更愿意使用 select * from sqlite_master where UPPER(name) = 'ROUTES' - Thupten
4
回答很好,但语法令人痛苦!应该改为'isTableExisting()'。 - Chris Hatton
无论我输入什么随机表名,getCount总是返回1。@Nikolay DS - Teja Nandamuri
1
这对我有用,但我不得不在查询中加入try/catch,否则当表不存在时,我的应用程序会崩溃。 - Braden Holt
显示剩余7条评论

55

我对Android SQLite API一无所知,但是如果你能直接使用SQL与它交互,你可以这样做:

create table if not exists mytable (col1 type, col2 type);

这将确保如果表已经存在,也不会抛出任何错误而始终创建该表。


这就是我在SQLiteOpenHelper类的onCreate方法中创建表的方式。在安卓中,推荐让该类创建表格,因为它可以允许应用程序自动更新其数据库,并且通常更加高效。不幸的是,执行代码的那个代码块似乎没有及时运行 :( - camperdave
这个非常好用,而且还支持索引,例如:"如果不存在则创建索引[...]"。 - Eric Fortier
5
这实际上是所问问题的最佳答案。 - The Original Android
1
但是这个问题并没有要求创建一个。 - 10101010

13

虽然已经有很多关于这个问题的好答案,但我想出了另一个更简单的解决方案。将您的查询放在try块中,并使用以下catch:

catch (SQLiteException e){
    if (e.getMessage().contains("no such table")){
            Log.e(TAG, "Creating table " + TABLE_NAME + "because it doesn't exist!" );
            // create table
            // re-run query, etc.
    }
}

它对我很有用!


1
把Log语句放在if()块里面会更好吧?如果SQLiteException是由于除了“没有这张表”之外的其他原因而被抛出,日志将表明您正在创建该表,而实际上并非如此。 - user513418
2
使用异常来控制流程是一件令人羞耻的事情,不应该教给别人。以那种方式检查消息更是如此。 - Nick Cardoso
如果适度使用,我认为在像上面描述的用例中使用异常没有任何问题。当然,这并不是什么让我感到难为情的事情。 - robguinness
我认为e.getMessage().contains("no such table")比使用异常来控制流程更糟糕。两者都是不好的风格,但解析特定文本的错误消息甚至是非常糟糕的风格。 - jox

11

这是我所做的:

/* open database, if doesn't exist, create it */
SQLiteDatabase mDatabase = openOrCreateDatabase("exampleDb.db", SQLiteDatabase.CREATE_IF_NECESSARY,null);

Cursor c = null;
boolean tableExists = false;
/* get cursor on it */
try
{
    c = mDatabase.query("tbl_example", null,
        null, null, null, null, null);
        tableExists = true;
}
catch (Exception e) {
    /* fail */
    Log.d(TAG, tblNameIn+" doesn't exist :(((");
}

return tableExists;

8

没错,我的编辑理论是正确的:导致onCreate方法无法运行的问题是SQLiteOpenHelper对象应该引用数据库,而不是每个表都有一个单独的对象。将两个表都打包到一个SQLiteOpenHelper中解决了这个问题。


4
基于其他人在此处所写的内容,以下是 Kotlin 解决方案:
    fun isTableExists(database: SQLiteDatabase, tableName: String): Boolean {
        database.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '$tableName'", null)?.use {
            return it.count > 0
        } ?: return false
    }

3
 // @param db, readable database from SQLiteOpenHelper

 public boolean doesTableExist(SQLiteDatabase db, String tableName) {
        Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '" + tableName + "'", null);

    if (cursor != null) {
        if (cursor.getCount() > 0) {
            cursor.close();
            return true;
        }
        cursor.close();
    }
    return false;
}
  • sqlite维护sqlite_master表,其中包含数据库中所有表和索引的信息。
  • 所以在这里,我们只需要在其上运行SELECT命令,如果存在该表,我们将获得一个游标计数为1。

2
您提到您创建了一个继承了 SQLiteOpenHelper 并实现了 onCreate 方法的类。您确定了所有与数据库相关的操作都是使用该类完成的吗?您应该只通过 SQLiteOpenHelper#getWritableDatabasegetReadableDatabase 方法获得 SQLiteDatabase 对象,否则当需要时不会调用 onCreate 方法。如果您已经这样做了,请检查一下是否正在调用 SQLiteOpenHelper#onUpgrade 方法。如果是,则表示在某个时间点更改了数据库版本号,但是在那时表并没有被正确地创建。
此外,您可以通过确保关闭所有连接并调用 Context#deleteDatabase,然后使用 SQLiteOpenHelper 给您一个新的数据库对象来强制重新创建数据库。

嗯,我是通过getWritableDatabase()方法获取我的数据库对象的,抱歉我忘记说了。另外,我确定onUpgrade()方法没有被调用,因为该方法的第一行是一个Log.d(...)调用,但我在数据库中没有看到它。我将尝试删除整个数据库文件,看看是否能解决这个问题… - camperdave
不幸的是,删除整个数据库(我使用根浏览器清除文件)没有起作用。我的应用程序中使用了两个表格 - 其中一个初始化完美,然而一直给我麻烦的另一个却没有。 - camperdave

0
 public boolean isTableExists(String tableName) {
    boolean isExist = false;
    Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '" + tableName + "'", null);
    if (cursor != null) {
        if (cursor.getCount() > 0) {
            isExist = true;
        }
        cursor.close();
    }
    return isExist;
}

0

no such table exists: error 出现的原因是,当你创建一个带有一个表的数据库后,每当你在同一数据库中创建表时,就会出现此错误。

要解决此错误,您必须创建一个新的数据库,并在 onCreate() 方法内创建同一数据库中的多个表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接