我想删除或添加sqlite数据库中的列
我正在使用以下查询删除列。
ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME
但是它给出了错误
System.Data.SQLite.SQLiteException: SQLite error
near "DROP": syntax error
答案 0 :(得分:314)
SQLite支持ALTER TABLE的有限子集。 SQLite中的ALTER TABLE命令允许用户重命名表或向现有表添加新列。无法重命名列,删除列,或从表中添加或删除约束。
你可以:
答案 1 :(得分:42)
我已经基于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/
答案 2 :(得分:17)
正如其他人指出的那样
无法重命名列,删除列或添加或 从表中删除约束。
来源:http://www.sqlite.org/lang_altertable.html
虽然您始终可以创建新表,然后删除旧表。 我将尝试用一个例子解释this workaround。
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_person
重命名为person
sqlite> DROP TABLE IF EXISTS person ;
sqlite> ALTER TABLE new_person RENAME TO person ;
sqlite>
现在如果你做.schema
,你会看到
sqlite>.schema
CREATE TABLE "person"(
id INTEGER PRIMARY KEY,
first_name TEXT,
last_name TEXT,
age INTEGER
);
答案 3 :(得分:12)
http://www.sqlite.org/lang_altertable.html
如图所示,仅支持ADD COLUMN。但是有一种(有点沉重的)解决方法:http://www.sqlite.org/faq.html#q11
答案 4 :(得分:8)
SQLite 3.35.0 引入了对 ALTER TABLE DROP COLUMN
的支持。
DROP COLUMN 语法用于从表中删除现有列。 DROP COLUMN 命令从表中删除指定列,并重写整个表以清除与该列关联的数据柱子。 DROP COLUMN 命令仅在该列未被架构的任何其他部分引用并且不是 PRIMARY KEY 并且没有 UNIQUE 约束时才有效。
以下语法将有效:
ALTER TABLE <TABLENAME> DROP COLUMN <COLUMNNAME>;
ALTER TABLE <TABLENAME> DROP <COLUMNNAME>;
答案 5 :(得分:4)
我重写了@Udinic answer,以便代码自动生成表格创建查询 。它也不需要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();
}
}
答案 6 :(得分:4)
我们无法删除SQLite 3中的特定列。请参阅FAQ。
答案 7 :(得分:3)
正如其他人所指出的,sqlite的ALTER TABLE
语句不支持DROP COLUMN
,而执行此操作的标准配方并不保留约束和&amp;指数。
这里有一些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
答案 8 :(得分:1)
由于SQLite对ALTER TABLE的支持有限,因此您只能在表的末尾添加ADD列或在SQLite中使用CHANGE TABLE_NAME。
以下是如何从SQLITE中删除列的最佳答案?
答案 9 :(得分:1)
有一次这不被直接支持,你需要follow a four-step process:(1)创建一个临时表,(2)复制数据,(3)删除旧表,然后(4)重命名临时表。
但是既然这些功能得到支持,您需要做的就是升级 SQLite。
ALTER TABLE DROP COLUMN
ALTER TABLE RENAME COLUMN
ALTER TABLE ADD COLUMN
请注意,仍有一些边缘情况可能不起作用,例如,您不能删除主键列。有关更多详细信息,请参阅文档。当这些 ALTER TABLE … COLUMN
语句不起作用时,您可以退回到四步流程。
顺便说一下,四步过程实际上是文档中的十二步过程。但其中四个步骤非常重要,很容易出错,并且在这些文档中特别指出。
答案 10 :(得分:1)
我猜您想做的是数据库迁移。 SQLite中不存在“删除”列。但是,您可以使用ALTER表查询添加额外的列。
答案 11 :(得分:1)
根据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)
此脚本首先生成随机临时表,并插入除了将要删除的列之外的必要列的数据。然后根据临时表恢复原始表并删除临时表。
答案 12 :(得分:0)
添加列的示例: -
alter table student add column TOB time;
学生 table_name , TOB column_name 即可添加。
它正在运行和测试。
答案 13 :(得分: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");
答案 14 :(得分:0)
您可以使用SQlite管理员更改列名称。 右键单击表名并选择编辑表。在这里您可以找到表结构,并可以轻松地重命名。
答案 15 :(得分:0)
我的解决方案,只需要调用此方法。
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;
}
答案 16 :(得分:0)
我改进了user2638929 answer,现在它可以保留列类型,主键,默认值等。
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
答案 17 :(得分:0)
你可以使用Sqlitebrowser。在浏览器模式下,对于相应的数据库和表,在选项卡数据库结构下,在“修改表”选项后面,可以删除相应的列。
答案 18 :(得分:0)
DB Browser for SQLite允许您添加或删除列。
答案 19 :(得分:0)
Kotlin 解决方案,基于 here ,但也确保临时表不存在:
object DbUtil {
@JvmStatic
fun dropColumns(database: SQLiteDatabase, tableName: String, columnsToRemove: Collection<String>) {
val columnNames: MutableList<String> = ArrayList()
val columnNamesWithType: MutableList<String> = ArrayList()
val primaryKeys: MutableList<String> = ArrayList()
val query = "pragma table_info($tableName);"
val cursor = database.rawQuery(query, null)
while (cursor.moveToNext()) {
val columnName = cursor.getString(cursor.getColumnIndex("name"))
if (columnsToRemove.contains(columnName)) {
continue
}
val columnType = cursor.getString(cursor.getColumnIndex("type"))
val isNotNull = cursor.getInt(cursor.getColumnIndex("notnull")) == 1
val isPk = cursor.getInt(cursor.getColumnIndex("pk")) == 1
columnNames.add(columnName)
var tmp = "`$columnName` $columnType "
if (isNotNull) {
tmp += " NOT NULL "
}
when (cursor.getType(cursor.getColumnIndex("dflt_value"))) {
Cursor.FIELD_TYPE_STRING -> {
tmp += " DEFAULT \"${cursor.getString(cursor.getColumnIndex("dflt_value"))}\" "
}
Cursor.FIELD_TYPE_INTEGER -> {
tmp += " DEFAULT ${cursor.getInt(cursor.getColumnIndex("dflt_value"))} "
}
Cursor.FIELD_TYPE_FLOAT -> {
tmp += " DEFAULT ${cursor.getFloat(cursor.getColumnIndex("dflt_value"))} "
}
}
columnNamesWithType.add(tmp)
if (isPk) {
primaryKeys.add("`$columnName`")
}
}
cursor.close()
val columnNamesSeparated = TextUtils.join(", ", columnNames)
if (primaryKeys.size > 0) {
columnNamesWithType.add("PRIMARY KEY(${TextUtils.join(", ", primaryKeys)})")
}
val columnNamesWithTypeSeparated = TextUtils.join(", ", columnNamesWithType)
database.beginTransaction()
try {
var newTempTableName: String
var counter = 0
while (true) {
newTempTableName = "${tableName}_old_$counter"
if (!isTableExists(database, newTempTableName))
break
++counter
}
database.execSQL("ALTER TABLE $tableName RENAME TO $newTempTableName;")
database.execSQL("CREATE TABLE $tableName ($columnNamesWithTypeSeparated);")
database.execSQL("INSERT INTO $tableName ($columnNamesSeparated) SELECT $columnNamesSeparated FROM $newTempTableName;")
database.execSQL("DROP TABLE ${newTempTableName};")
database.setTransactionSuccessful()
} finally {
database.endTransaction()
}
}
@JvmStatic
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
}
}
答案 20 :(得分:0)
对不同问题的回答是针对修改一列,但我相信如果你有很多专栏并且不想要,那么答案的一部分也会产生有用的方法为INSERT语句手动重新键入大部分内容:
https://stackoverflow.com/a/10385666
您可以按照上面的链接中的描述转储数据库,然后抓取&#34;创建表&#34;声明和&#34;插入&#34;该转储中的模板,然后按照SQLite FAQ条目中的说明进行操作&#34;如何在SQLite中添加或删除现有表中的列。&#34; (FAQ链接到本页的其他地方。)
答案 21 :(得分:0)
作为替代方案:
如果您有一个包含架构的表
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且没有约束。要保留这些,请使用其他描述的方法创建新表,或使用temporary table作为中间人。
答案 22 :(得分:-1)
您现在也可以使用DB浏览器来管理SQLite