我想在sqlite数据库中删除或添加列

我正在使用以下查询删除列。

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

但它会产生错误

System.Data.SQLite.SQLiteException: SQLite error
near "DROP": syntax error

当前回答

这个不同问题的答案是针对修改列的,但我相信,如果你有很多列,并且不想在INSERT语句中手动重新输入大部分列,部分答案也可以产生有用的方法:

https://stackoverflow.com/a/10385666

您可以按照上面链接中描述的方法转储数据库,然后从该转储中获取“create table”语句和“insert”模板,然后按照SQLite FAQ条目“如何从SQLite中现有的表中添加或删除列”中的说明进行操作。(常见问题在本页其他地方有链接。)

其他回答

http://www.sqlite.org/lang_altertable.html

正如您在图中看到的,只支持ADD COLUMN。不过,有一个(有点沉重的)变通方法:http://www.sqlite.org/faq.html#q11

有一段时间不直接支持这一点,您需要遵循以下四个步骤:(1)创建一个temporary_table,(2)复制数据,(3)删除旧表,然后(4)重命名temporary_table。

但是现在支持这些特性,您所需要做的就是升级SQLite。

3.35.0 from 2021-03-12添加ALTER TABLE DROP COLUMN 3.25.0 from 2018-09-15新增ALTER TABLE RENAME COLUMN 3.2.0在2005-03-21添加了ALTER TABLE ADD COLUMN

请注意,仍然有一些边缘情况下,这些可能不起作用,例如,您不能删除主键列。有关详细信息,请参阅文档。当这些ALTER TABLE…COLUMN语句不起作用时,您可以退回到四步过程。

顺便说一下,文档中的四步流程实际上是十二步流程。但其中四个步骤非常重要,很容易出错,在这些文档中特别指出。

基于http://www.sqlite.org/faq.html#q11上的信息在Python中实现。

import sqlite3 as db
import random
import string

QUERY_TEMPLATE_GET_COLUMNS = "PRAGMA table_info(@table_name)"
QUERY_TEMPLATE_DROP_COLUMN = """
  BEGIN TRANSACTION;
  CREATE TEMPORARY TABLE @tmp_table(@columns_to_keep);
  INSERT INTO @tmp_table SELECT @columns_to_keep FROM @table_name;
  DROP TABLE @table_name;
  CREATE TABLE @table_name(@columns_to_keep);
  INSERT INTO @table_name SELECT @columns_to_keep FROM @tmp_table;
  DROP TABLE @tmp_table;
  COMMIT;
"""

def drop_column(db_file, table_name, column_name):
    con = db.connect(db_file)
    QUERY_GET_COLUMNS = QUERY_TEMPLATE_GET_COLUMNS.replace("@table_name", table_name)
    query_res = con.execute(QUERY_GET_COLUMNS).fetchall()
    columns_list_to_keep = [i[1] for i in query_res if i[1] != column_name]
    columns_to_keep = ",".join(columns_list_to_keep)
    tmp_table = "tmp_%s" % "".join(random.sample(string.ascii_lowercase, 10))
    QUERY_DROP_COLUMN = QUERY_TEMPLATE_DROP_COLUMN.replace("@table_name", table_name)\
        .replace("@tmp_table", tmp_table).replace("@columns_to_keep", columns_to_keep)
    con.executescript(QUERY_DROP_COLUMN)
    con.close()

drop_column(DB_FILE, TABLE_NAME, COLUMN_NAME)

这个脚本首先创建随机的临时表,并只插入必要列的数据,除了将要删除的列。然后根据临时表恢复原表,删除临时表。

我已经改进了user2638929回答,现在它可以保留列类型,主键,默认值等。

public static void dropColumns(SQLiteDatabase database, String tableName, Collection<String> columnsToRemove){
    List<String> columnNames = new ArrayList<>();
    List<String> columnNamesWithType = new ArrayList<>();
    List<String> primaryKeys = new ArrayList<>();
    String query = "pragma table_info(" + tableName + ");";
    Cursor cursor = database.rawQuery(query,null);
    while (cursor.moveToNext()){
        String columnName = cursor.getString(cursor.getColumnIndex("name"));

        if (columnsToRemove.contains(columnName)){
            continue;
        }

        String columnType = cursor.getString(cursor.getColumnIndex("type"));
        boolean isNotNull = cursor.getInt(cursor.getColumnIndex("notnull")) == 1;
        boolean isPk = cursor.getInt(cursor.getColumnIndex("pk")) == 1;

        columnNames.add(columnName);
        String tmp = "`" + columnName + "` " + columnType + " ";
        if (isNotNull){
            tmp += " NOT NULL ";
        }

        int defaultValueType = cursor.getType(cursor.getColumnIndex("dflt_value"));
        if (defaultValueType == Cursor.FIELD_TYPE_STRING){
            tmp += " DEFAULT " + "\"" + cursor.getString(cursor.getColumnIndex("dflt_value")) + "\" ";
        }else if(defaultValueType == Cursor.FIELD_TYPE_INTEGER){
            tmp += " DEFAULT " + cursor.getInt(cursor.getColumnIndex("dflt_value")) + " ";
        }else if (defaultValueType == Cursor.FIELD_TYPE_FLOAT){
            tmp += " DEFAULT " + cursor.getFloat(cursor.getColumnIndex("dflt_value")) + " ";
        }
        columnNamesWithType.add(tmp);
        if (isPk){
            primaryKeys.add("`" + columnName + "`");
        }
    }
    cursor.close();

    String columnNamesSeparated = TextUtils.join(", ", columnNames);
    if (primaryKeys.size() > 0){
        columnNamesWithType.add("PRIMARY KEY("+ TextUtils.join(", ", primaryKeys) +")");
    }
    String columnNamesWithTypeSeparated = TextUtils.join(", ", columnNamesWithType);

    database.beginTransaction();
    try {
        database.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
        database.execSQL("CREATE TABLE " + tableName + " (" + columnNamesWithTypeSeparated + ");");
        database.execSQL("INSERT INTO " + tableName + " (" + columnNamesSeparated + ") SELECT "
                + columnNamesSeparated + " FROM " + tableName + "_old;");
        database.execSQL("DROP TABLE " + tableName + "_old;");
        database.setTransactionSuccessful();
    }finally {
        database.endTransaction();
    }
}

PS:这里我使用的是android.arch.persistence.db。支持sqlitedatabase,但你可以很容易地修改它使用android.database.sqlite.SQLiteDatabase

你可以使用Sqlitebrowser。在浏览器模式下,对于各自的数据库和表,在选项卡-database结构下,选择Modify table后,可以删除各自的列。