如何在SQLite中检查表是否存在?

1106

如何在SQLite中可靠地检查特定用户表是否存在?

我不是在询问像检查“select *”语句是否返回错误之类的不可靠方法(这是一个好主意吗?)。

原因如下:

在我的程序中,如果某些表不存在,我需要创建并填充它们。

如果这些表已经存在,我需要更新一些表。

是否应该采取其他方式来表示所涉及的表已经被创建 - 比如,在我的程序初始化/设置文件中创建/放置/设置某个标志或者类似操作?

还是我的方法有道理?


Ravens的评论 SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type="table" AND name="table_name");似乎将结果缩减为布尔值结果(只能是0或1,没有其他值) - undefined
31个回答

1214

我错过了那个常见问题解答条目。

不管怎样,以后参考一下,完整的查询语句是:

SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';

其中{table_name}是要检查的表的名称。

参考文档部分:数据库文件格式。2.6. SQL 数据库架构的存储

  • 这将返回一个具有指定名称的表的列表;也就是说,游标将具有 0(不存在)或 1(存在)的计数。

9
哪些 SQLite 文档涵盖了这些系统表? - Pawel Veselov
34
@Pawel Veselov: 这篇文章的 "SQLite数据库文件格式" 部分: http://www.sqlite.org/fileformat2.html - Bryan Oakley
1
如果你使用Adobe Air SQLite,这个方法是不行的,下面是一个更好的替代方法,适用于你在AS3/Air中编程的情况:https://dev59.com/bVPTa4cB1Zd3GeqPgSDV - CenterOrbit
19
对于临时表而言,这样做是行不通的。临时表位于"sqlite_temp_master"中。 - PatchyFog
3
自从SQLite 3.33.0版本以来,该表的新名称为sqlite_schema。旧名称仍然有效,但是答案可能需要更新以提及新名称。 - Igor Levicki
注意:尽管引用的文档中提到:“由于历史和操作上的考虑,"sqlite_schema"表有时也可以被称为以下任一别名:sqlite_master sqlite_temp_schema sqlite_temp_master*"但我无法从sqlite_schema中进行选择;然而,sqlite_master有效(使用Perl DBI 5.18.2的sqlite 3.39.3)。 - U. Windl

661

如果你使用的是SQLite 3.3以上版本,你可以轻松地创建一个表:

create table if not exists TableName (col1 typ1, ..., colN typN)

同样的方式,你可以使用以下代码仅在表存在时才删除表:

drop table if exists TableName

14
索引也有类似的语法结构:**如果不存在,则为TableName表的col1列创建索引:create index if not exists TableName_col1 on TableName(col1)**。 - lowtech
41
如果问题措辞不同,这个答案就不应被接受。原帖子并没有问如何在删除或创建表前检查。如果你要查询一个可能不存在的表怎么办?这正是我现在遇到的问题,而被接受的答案最适合解决这个更广泛的问题陈述。这是一个很好的快速替代方案。 - Dagrooms

221

一种变化是使用 SELECT COUNT(*) 而不是 SELECT NAME,即:

SELECT count(*) FROM sqlite_master WHERE type='table' AND name='table_name';

如果表不存在,则此代码将返回0;如果存在,则返回1。在编程中,由于数字结果更快/更易处理,因此这可能很有用。以下示例说明了如何在Android中使用SQLiteDatabase、Cursor和带参数的rawQuery来执行此操作。

boolean tableExists(SQLiteDatabase db, String tableName)
{
    if (tableName == null || db == null || !db.isOpen())
    {
        return false;
    }
    Cursor cursor = db.rawQuery(
       "SELECT COUNT(*) FROM sqlite_master WHERE type = ? AND name = ?",
       new String[] {"table", tableName}
    );
    if (!cursor.moveToFirst())
    {
        cursor.close();
        return false;
    }
    int count = cursor.getInt(0);
    cursor.close();
    return count > 0;
}

43
我认为使用 "SELECT 1" 会更快。 - PatchyFog
为什么cursor.getInt(0)等于数据库中记录的数量? - Semyon Danilov
3
我们正在计算 SQLite 模式中表格出现的次数。计数为0表示该表不存在。计数为1表示该表存在。这是计数的唯二预期值。 - Stephen Quan
1
虽然从 COUNT(*) 返回的数字很容易处理,但返回行是否存在则更容易;如果有一行,则它存在,如果没有行,则不存在。(您已经在 moveToFirst 中检查了失败,因此工作将在那一点上完成。) - dash-tom-bang
重新考虑一下,那个返回假的情况会发生吗?看起来高度不可能。 - Dave Thomas
6
“SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type="table" AND name="table_name")” 看起来真的可以将结果缩减为布尔值(只有0或1,没有其他)。 - Raven

56

你可以尝试:

SELECT name FROM sqlite_master WHERE name='table_name'

9
"type = table" 这样的类型会很有用。 - mafu
如果使用C#,不要在SQLiteReader reader = cmd.ExecuteReader();中使用此命令,然后执行dt.Load(reader)(其中dtDataTable)。我发现如果找不到表格,则会在.Load()上出现Object reference is not an instance of an object异常。相反,使用SQLiteDataAdapter adapter = new SQLiteDataAdapter(cmd);,然后执行adapter.Fill(ds),其中dsDataSet。然后您可以查看ds.Tables.Count > 0并返回ds.Tables[0](如果是这样,否则返回null)。然后您可以检查该DataTable是否为null,如果dt.Rows != null,并且如果dt.Rows.Count > 0 - vapcguy

49

39

使用:

PRAGMA table_info(your_table_name)
如果结果表为空,则说明your_table_name不存在。
文档:

PRAGMA schema.table_info(table-name);

此pragma针对命名表中的每个列返回一行。结果集中的列包括列名称、数据类型、该列是否可以为NULL以及该列的默认值。结果集中的“pk”列对于不是主键的列为零,对于是主键的列,则是主键中该列的索引。

在table_info pragma中命名的表也可以是视图。

示例输出:
cid|name|type|notnull|dflt_value|pk
0|id|INTEGER|0||1
1|json|JSON|0||0
2|name|TEXT|0||0

另一种方法可能是使用 select * from pragma_table_list where name="your_table_name"; 这是sqlite 3.37中的新pragma。https://sqlite.org/pragma.html#pragma_table_list - Harish Ganesan

38

SQLite表名不区分大小写,但默认情况下比较是区分大小写的。为了使其在所有情况下正常工作,您需要添加COLLATE NOCASE

SELECT name FROM sqlite_master WHERE type='table' AND name='table_name' COLLATE NOCASE

36

如果您收到“表已经存在”错误,请按照以下方式更改SQL字符串:

CREATE table IF NOT EXISTS table_name (para1,para2);

通过这种方式,您可以避免异常情况。


24

如果你正在使用fmdb,我认为你可以只需导入FMDatabaseAdditions并使用布尔函数:

[yourfmdbDatabase tableExists:tableName].

1
请确保导入"FMDatabaseAdditions.h"文件才能使用此方法,否则你会想知道为什么它被移除了! :) - Will
1
虽然这可能是一个正确的答案,但问题是关于sqlite而不是特定语言中的特定库。我认为答案应该是提供SQL代码,而不是调用库的某个方法。 - nacho4d

13

以下代码返回1,如果表存在,返回0,如果表不存在。

SELECT CASE WHEN tbl_name = "name" THEN 1 ELSE 0 END FROM sqlite_master WHERE tbl_name = "name" AND type = "table"

2
如果表不存在,这仍然会返回空值,因为where条件阻止了任何结果。 - David Gausmann

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