如何在SQLITE中删除或添加列?


240

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

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

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

但这给了错误

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

Answers:


353

ALTER TABLE SQLite

SQLite支持ALTER TABLE的有限子集。SQLite中的ALTER TABLE命令允许用户重命名表或向现有表添加新列。无法重命名列,删除列或从表中添加或删除约束。

您可以:

  1. 创建一个新表作为您要更改的表,
  2. 复制所有数据,
  3. 放下旧桌子,
  4. 重命名新的。

47
stackoverflow.com/a/5987838/1578528提供了执行任务的基本示例。
bikram990

4
在执行此序列之前,以及在有外部表引用该表的情况下,必须调用该表PRAGMA foreign_keys=OFF。在这种情况下,执行此序列后必须调用PRAGMA foreign_keys=ON才能重新启用外键。
PazO

您还如何复制快速键和索引?
乔纳森

只要您先创建一个新表而不是从select中创建表,它将拥有所有这些内容。
约翰·罗德,

1
在较新的SQLite版本中,RENAME COLUMN受支持。🎉sqlite.org/releaselog/ 3_25_0.html
Grogs

46

我已经基于Sqlite的推荐方法编写了Java实现:

private void dropColumn(SQLiteDatabase db,
        ConnectionSource connectionSource,
        String createTableCmd,
        String tableName,
        String[] colsToRemove) throws java.sql.SQLException {

    List<String> updatedTableColumns = getTableColumns(tableName);
    // Remove the columns we don't want anymore from the table's list of columns
    updatedTableColumns.removeAll(Arrays.asList(colsToRemove));

    String columnsSeperated = TextUtils.join(",", updatedTableColumns);

    db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");

    // Creating the table on its new format (no redundant columns)
    db.execSQL(createTableCmd);

    // Populating the table with the data
    db.execSQL("INSERT INTO " + tableName + "(" + columnsSeperated + ") SELECT "
            + columnsSeperated + " FROM " + tableName + "_old;");
    db.execSQL("DROP TABLE " + tableName + "_old;");
}

要获取表的列,我使用了“ PRAGMA table_info”:

public List<String> getTableColumns(String tableName) {
    ArrayList<String> columns = new ArrayList<String>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = getDB().rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}

我实际上是在我的博客上写的,您可以在此处看到更多说明:

http://udinic.wordpress.com/2012/05/09/sqlite-drop-column-support/


1
这是很慢的,不是吗?大数据表?
Joran Beasley

2
如果这是在单个事务中完成,那将更好,而不是潜在地允许其他代码以过渡状态查看事物。
Donal Fellows

我通常在升级数据库时运行此代码,而其他代码却无法同时运行。您可以创建一个事务并在其中执行所有这些命令。
Udinic

3
我非常确定,如果您使用此解决方案,那么结果表上的列将完全裸露-没有类型信息,PK,FK,默认值,唯一或检查约束将保留。它导入到新表中的全部就是列名。此外,由于它在运行前不会禁用外键,因此其他表中的数据也可能被破坏。
ACK_stoverflow,2014年

4
另外,INSERT您也可以执行以下操作来创建新表,而不是声明:"CREAT TABLE" + tableName + "AS SELECT " + columnsSeperated + " FROM " + tableName + "_old;"
Robert Robert

26

正如其他人指出的

无法重命名列,删除列或从表中添加或删除约束。

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

虽然您始终可以创建一个新表,然后删除旧表。我将尝试通过一个例子来解释这种解决方法

sqlite> .schema
CREATE TABLE person(
 id INTEGER PRIMARY KEY, 
 first_name TEXT,
 last_name TEXT, 
 age INTEGER, 
 height INTEGER
);
sqlite> select * from person ; 
id          first_name  last_name   age         height    
----------  ----------  ----------  ----------  ----------
0           john        doe         20          170       
1           foo         bar         25          171       

现在您height要从该表中删除该列。

创建另一个名为 new_person

sqlite> CREATE TABLE new_person(
   ...>  id INTEGER PRIMARY KEY, 
   ...>  first_name TEXT, 
   ...>  last_name TEXT, 
   ...>  age INTEGER 
   ...> ) ; 
sqlite> 

现在复制旧表中的数据

sqlite> INSERT INTO new_person
   ...> SELECT id, first_name, last_name, age FROM person ;
sqlite> select * from new_person ;
id          first_name  last_name   age       
----------  ----------  ----------  ----------
0           john        doe         20        
1           foo         bar         25        
sqlite>

现在,删除person表格并重命名new_personperson

sqlite> DROP TABLE IF EXISTS person ; 
sqlite> ALTER TABLE new_person RENAME TO person ;
sqlite>

所以现在如果您执行a .schema,您将看到

sqlite>.schema
CREATE TABLE "person"(
 id INTEGER PRIMARY KEY, 
 first_name TEXT, 
 last_name TEXT, 
 age INTEGER 
);

国外参考文献呢?如果删除其他表正在使用的表,Oracle会抱怨。
Leos Literak '16

6
我可以说你是一个真正的程序员。您在john doe之后
就没

1
CREATE TABLE new_person AS SELECT id, first_name, last_name, age FROM person;
Clay



4

正如其他人所指出的那样,源码的ALTER TABLE声明并没有支持DROP COLUMN,标准配方要做到这一点不保留的约束和索引。

这是一些在保持所有关键约束和索引的同时进行通用处理的python代码。

使用前请备份您的数据库!此函数依赖于篡改原始的CREATE TABLE语句,并且可能有点不安全-例如,如果标识符包含嵌入式逗号或括号,它将执行错误的操作。

如果有人愿意提供一种更好的方法来解析SQL,那就太好了!

更新我发现了使用开源sqlparse软件包进行解析的更好方法。如果有任何兴趣,我将在这里发布,只需发表评论即可...

import re
import random

def DROP_COLUMN(db, table, column):
    columns = [ c[1] for c in db.execute("PRAGMA table_info(%s)" % table) ]
    columns = [ c for c in columns if c != column ]
    sql = db.execute("SELECT sql from sqlite_master where name = '%s'" 
        % table).fetchone()[0]
    sql = format(sql)
    lines = sql.splitlines()
    findcol = r'\b%s\b' % column
    keeplines = [ line for line in lines if not re.search(findcol, line) ]
    create = '\n'.join(keeplines)
    create = re.sub(r',(\s*\))', r'\1', create)
    temp = 'tmp%d' % random.randint(1e8, 1e9)
    db.execute("ALTER TABLE %(old)s RENAME TO %(new)s" % { 
        'old': table, 'new': temp })
    db.execute(create)
    db.execute("""
        INSERT INTO %(new)s ( %(columns)s ) 
        SELECT %(columns)s FROM %(old)s
    """ % { 
        'old': temp,
        'new': table,
        'columns': ', '.join(columns)
    })  
    db.execute("DROP TABLE %s" % temp)

def format(sql):
    sql = sql.replace(",", ",\n")
    sql = sql.replace("(", "(\n")
    sql = sql.replace(")", "\n)")
    return sql

它也维护表的外键吗?
Lasse V. Karlsen 2014年

@ LasseV.Karlsen我做了一些测试,它应该维护外键约束,因为这些约束似乎是由表名强制执行的。
spam_eggs 2014年

如何从Java运行它?
Leos Literak '16

4

我重写了@Udinic答案,以便代码自动生成表创建查询。它也不需要ConnectionSource。它还必须在事务内执行此操作。

public static String getOneTableDbSchema(SQLiteDatabase db, String tableName) {
    Cursor c = db.rawQuery(
            "SELECT * FROM `sqlite_master` WHERE `type` = 'table' AND `name` = '" + tableName + "'", null);
    String result = null;
    if (c.moveToFirst()) {
        result = c.getString(c.getColumnIndex("sql"));
    }
    c.close();
    return result;
}

public List<String> getTableColumns(SQLiteDatabase db, String tableName) {
    ArrayList<String> columns = new ArrayList<>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = db.rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}

private void dropColumn(SQLiteDatabase db, String tableName, String[] columnsToRemove) {
    db.beginTransaction();
    try {
        List<String> columnNamesWithoutRemovedOnes = getTableColumns(db, tableName);
        // Remove the columns we don't want anymore from the table's list of columns
        columnNamesWithoutRemovedOnes.removeAll(Arrays.asList(columnsToRemove));

        String newColumnNamesSeparated = TextUtils.join(" , ", columnNamesWithoutRemovedOnes);
        String sql = getOneTableDbSchema(db, tableName);
        // Extract the SQL query that contains only columns
        String oldColumnsSql = sql.substring(sql.indexOf("(")+1, sql.lastIndexOf(")"));

        db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
        db.execSQL("CREATE TABLE `" + tableName + "` (" + getSqlWithoutRemovedColumns(oldColumnsSql, columnsToRemove)+ ");");
        db.execSQL("INSERT INTO " + tableName + "(" + newColumnNamesSeparated + ") SELECT " + newColumnNamesSeparated + " FROM " + tableName + "_old;");
        db.execSQL("DROP TABLE " + tableName + "_old;");
        db.setTransactionSuccessful();
    } catch {
        //Error in between database transaction 
    } finally {
        db.endTransaction();
    }


}

3

SQLite的数据库浏览器允许您添加或删除列。

在主视图中,tab Database Structure,单击表名。Modify Table启用按钮,这将打开一个新窗口,您可以在其中选择列/字段并将其删除。


2

您可以使用Sqlitebrowser。在浏览器模式下,对于相应的数据库和表,在选项卡-database结构下,在选项Modify Table之后,可以删除相应的列。


2

我改善了user2638929的答案,现在它可以保留列类型,主键,默认值等。

private static void dropColumn(SupportSQLiteDatabase database, String tableName, List<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.query(query);
    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.SupportSQLiteDatabase,但是您可以轻松地对其进行修改以供使用android.database.sqlite.SQLiteDatabase


2

我猜您想做的是数据库迁移。SQLite中不存在“删除”列。但是,您可以使用ALTER表查询添加额外的列。


1

您可以使用SQlite Administrator更改列名称。右键单击表名称并选择编辑表。在这里您将找到表结构,并且可以轻松地对其进行重命名。



1

作为备选:

如果您有带模式的表

CREATE TABLE person(
  id INTEGER PRIMARY KEY,
  first_name TEXT,
  last_name TEXT,
  age INTEGER,
  height INTEGER
);

您可以使用CREATE TABLE...AS类似的语句CREATE TABLE person2 AS SELECT id, first_name, last_name, age FROM person;,即省略不需要的列。然后删除原始person表并重命名新表。

请注意,此方法产生的表没有PRIMARY KEY,也没有约束。要保留这些信息,请使用其他描述的方法来创建新表,或使用临时表作为中间


1

这个不同问题的答案是针对修改列,但是我相信,如果您有很多列并且不想为INSERT语句手动重新键入大多数列,那么部分答案也可以提供一种有用的方法:

https://stackoverflow.com/a/10385666

您可以按照上面的链接中所述转储数据库,然后从该转储中获取“创建表”语句和“插入”模板,然后按照SQLite FAQ条目“如何从现有数据库中添加或删除列”中的说明进行操作。 SQLite中的表格。” (常见问题解答链接在此页面的其他位置。)


实际上,我只是意识到转储默认情况下在插入中不包含列名。因此,仅使用.schema pragma来获取列名可能同样好,因为您将需要以任何一种方式删除类型声明。
burpgrass

1

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

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)

该脚本首先创建随机临时表,然后仅插入必要列的数据,除了将要删除的列。然后基于临时表还原原始表,并删除临时表。


1

我的解决方案,只需要调用此方法即可。

public static void dropColumn(SQLiteDatabase db, String tableName, String[] columnsToRemove) throws java.sql.SQLException {
    List<String> updatedTableColumns = getTableColumns(db, tableName);
    updatedTableColumns.removeAll(Arrays.asList(columnsToRemove));
    String columnsSeperated = TextUtils.join(",", updatedTableColumns);

    db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
    db.execSQL("CREATE TABLE " + tableName + " (" + columnsSeperated + ");");
    db.execSQL("INSERT INTO " + tableName + "(" + columnsSeperated + ") SELECT "
            + columnsSeperated + " FROM " + tableName + "_old;");
    db.execSQL("DROP TABLE " + tableName + "_old;");
}

以及获取列的辅助方法:

public static List<String> getTableColumns(SQLiteDatabase db, String tableName) {
    ArrayList<String> columns = new ArrayList<>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = db.rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}

此方法不保留colunm类型,因此我发布了代码的修改版本
Berdimurat Masaliev

0
public void DeleteColFromTable(String DbName, String TableName, String ColName){
    SQLiteDatabase db = openOrCreateDatabase(""+DbName+"", Context.MODE_PRIVATE, null);
    db.execSQL("CREATE TABLE IF NOT EXISTS "+TableName+"(1x00dff);");
    Cursor c = db.rawQuery("PRAGMA table_info("+TableName+")", null);
    if (c.getCount() == 0) {

    } else {
        String columns1 = "";
        String columns2 = "";
        while (c.moveToNext()) {
            if (c.getString(1).equals(ColName)) {
            } else {
                columns1 = columns1 + ", " + c.getString(1) + " " + c.getString(2);
                columns2 = columns2 + ", " + c.getString(1);
            }
            if (c.isLast()) {
                db.execSQL("CREATE TABLE IF NOT EXISTS DataBackup (" + columns1 + ");");
                db.execSQL("INSERT INTO DataBackup SELECT " + columns2 + " FROM "+TableName+";");
                db.execSQL("DROP TABLE "+TableName+"");
                db.execSQL("ALTER TABLE DataBackup RENAME TO "+TableName+";");
            }
        }
    }
}

然后调用一个方法

DeleteColFromTable("Database name","Table name","Col name which want to delete");


-2

添加列的示例:-

alter table student add column TOB time;

在这里,学生table_nameTOB是要添加的column_name

它正在工作并经过测试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.