ALTER TABLE添加列,如果不是SQLite中的EXISTS

dan*_*n04 77 sqlite alter-table

我们最近需要在一些现有的SQLite数据库表中添加列.这可以通过以下方式完成ALTER TABLE ADD COLUMN.当然,如果表已被更改,我们希望不管它.不幸的是,SQLite不支持一个IF NOT EXISTS子句ALTER TABLE.

我们当前的解决方法是执行ALTER TABLE语句并忽略任何"重复列名"错误,就像这个Python示例(但在C++中).

然而,我们通常的做法来建立数据库模式是有含.SQL脚本CREATE TABLE IF NOT EXISTSCREATE INDEX IF NOT EXISTS语句,可以使用执行sqlite3_execsqlite3命令行工具.我们不能放入ALTER TABLE这些脚本文件,因为如果该语句失败,它之后的任何内容都不会被执行.

我希望将表定义放在一个地方,而不是在.sql和.cpp文件之间进行拆分.有没有办法ALTER TABLE ADD COLUMN IF NOT EXISTS在纯SQLite SQL中编写变通方法?

MPe*_*ier 55

我有一个99%的纯SQL方法.我们的想法是对架构进行版本控制.您可以通过两种方式执行此操作:

  • 使用'user_version'pragma命令(PRAGMA user_version)存储数据库模式版本的增量编号.

  • 将您的版本号存储在您自己定义的表中.

通过这种方式,当软件启动时,它可以检查数据库模式,并在需要时运行ALTER TABLE查询,然后增加存储的版本.这比尝试各种更新"盲目"要好得多,特别是如果您的数据库增长并且多年来改变了几次.

  • `user_version`的初始值是多少?我假设为零,但很高兴看到记录. (6认同)
  • 当你有一个现有的数据库时,关于`user_version`初始值的问题是相关的,你之前从未使用过`user_version`,但是你想开始使用它,所以你需要假设sqlite将它设置为特定的初始值. (6认同)

ang*_*sen 29

一种解决方法是仅创建列并捕获列已存在时出现的异常/错误.添加多个列时,请将它们添加到单独的ALTER TABLE语句中,以便一个副本不会阻止创建其他列.

使用sqlite-net,我们做了类似的事情.它并不完美,因为我们无法区分重复的sqlite错误和其他sqlite错误.

Dictionary<string, string> columnNameToAddColumnSql = new Dictionary<string, string>
{
    {
        "Column1",
        "ALTER TABLE MyTable ADD COLUMN Column1 INTEGER"
    },
    {
        "Column2",
        "ALTER TABLE MyTable ADD COLUMN Column2 TEXT"
    }
};

foreach (var pair in columnNameToAddColumnSql)
{
    string columnName = pair.Key;
    string sql = pair.Value;

    try
    {
        this.DB.ExecuteNonQuery(sql);
    }
    catch (System.Data.SQLite.SQLiteException e)
    {
        _log.Warn(e, string.Format("Failed to create column [{0}]. Most likely it already exists, which is fine.", columnName));
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果低估,请评论原因. (8认同)
  • Code Nazis downvote working answers. (2认同)

Rob*_*key 26

SQLite还支持名为"table_info"的pragma语句,该语句在表中每列返回一行,其中包含列的名称(以及有关该列的其他信息).您可以在查询中使用它来检查缺少的列,如果不存在则更改表.

PRAGMA table_info(foo_table_name)
Run Code Online (Sandbox Code Playgroud)

http://www.sqlite.org/pragma.html#pragma_table_info

  • 如果您提供完成搜索的代码而不仅仅是链接,那么您的答案会更加出色. (26认同)
  • 是否有一些方法可以通过在较大的SQL语句的一部分中组合pragma来实现此目的,以便在不存在的情况下添加该列,否则只在单个查询中添加? (2认同)

小智 20

如果您在数据库升级语句中执行此操作,则最简单的方法是在尝试添加可能已存在的字段时捕获引发的异常.

try {
   db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN foo TEXT default null");
} catch (SQLiteException ex) {
   Log.w(TAG, "Altering " + TABLE_NAME + ": " + ex.getMessage());
}
Run Code Online (Sandbox Code Playgroud)

  • 我不喜欢异常风格的编程,但这非常干净。也许你让我有些摇摆。 (2认同)

Kru*_*hah 11

threre是一个PRAGMA的方法是table_info(table_name),它返回表的所有信息.

这是实现如何使用它来检查列是否存在,

    public boolean isColumnExists (String table, String column) {
         boolean isExists = false
         Cursor cursor;
         try {           
            cursor = db.rawQuery("PRAGMA table_info("+ table +")", null);
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    if (column.equalsIgnoreCase(name)) {
                        isExists = true;
                        break;
                    }
                }
            }

         } finally {
            if (cursor != null && !cursor.isClose()) 
               cursor.close();
         }
         return isExists;
    }
Run Code Online (Sandbox Code Playgroud)

您也可以在不使用循环的情况下使用此查询,

cursor = db.rawQuery("PRAGMA table_info("+ table +") where name = " + column, null);
Run Code Online (Sandbox Code Playgroud)


小智 5

对于那些想要使用pragma table_info()'s 结果作为更大 SQL 的一部分的人。

select count(*) from
pragma_table_info('<table_name>')
where name='<column_name>';

Run Code Online (Sandbox Code Playgroud)

关键部分是使用pragma_table_info('<table_name>')而不是pragma table_info('<table_name>').


这个答案的灵感来自@Robert Hawkey 的回复。我将其发布为新答案的原因是我没有足够的声誉将其发布为评论。