Delete column from SQLite table
我有一个问题:我需要从SQLite数据库中删除一列。 我写了这个查询
1 | ALTER TABLE TABLE_NAME DROP COLUMN column_name |
但它不起作用。 请帮我。
来自:http://www.sqlite.org/faq.html:
(11) How do I add or delete columns from an existing table in SQLite.
SQLite has limited ALTER TABLE support that you can use to add a
column to the end of a table or to change the name of a table. If you
want to make more complex changes in the structure of a table, you
will have to recreate the table. You can save existing data to a
temporary table, drop the old table, create the new table, then copy
the data back in from the temporary table.For example, suppose you have a table named"t1" with columns names
"a","b", and"c" and that you want to delete column"c" from this
table. The following steps illustrate how this could be done:
1
2
3
4
5
6
7
8 BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;
不用删除备份表,只需对其重命名...
1 2 3 4 5 6 | BEGIN TRANSACTION; CREATE TABLE t1_backup(a,b); INSERT INTO t1_backup SELECT a,b FROM t1; DROP TABLE t1; ALTER TABLE t1_backup RENAME TO t1; COMMIT; |
为简单起见,为什么不从select语句创建备份表?
1 2 3 | CREATE TABLE t1_backup AS SELECT a, b FROM t1; DROP TABLE t1; ALTER TABLE t1_backup RENAME TO t1; |
仅当您可以在数据库浏览器(如用于SQLite的数据库浏览器)中打开数据库时,此选项才有效。
在SQLite的数据库浏览器中:
=>直接使用以下查询创建新表:
1 | CREATE TABLE TABLE_NAME (Column_1 TEXT,Column_2 TEXT); |
=>现在使用以下查询将数据从existing_table插入table_name中:
1 | INSERT INTO TABLE_NAME (Column_1,Column_2) FROM existing_table; |
=>现在通过以下查询删除现存表:
1 | DROP TABLE existing_table; |
我制作了一个Python函数,您可以在其中输入要删除的表和列作为参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def removeColumn(TABLE, COLUMN): COLUMNS = [] FOR ROW IN c.execute('PRAGMA table_info(' + TABLE + ')'): COLUMNS.append(ROW[1]) COLUMNS.remove(COLUMN) COLUMNS = str(COLUMNS) COLUMNS = COLUMNS.replace("[","(") COLUMNS = COLUMNS.replace("]",")") FOR i IN ["\'","(",")"]: COLUMNS = COLUMNS.replace(i,"") c.execute('CREATE TABLE temptable AS SELECT ' + COLUMNS + ' FROM ' + TABLE) c.execute('DROP TABLE ' + TABLE) c.execute('ALTER TABLE temptable RENAME TO ' + TABLE) conn.commit() |
根据Duda和MeBigFatGuy的答案的信息,如果表上有外键,这将不起作用,但是可以用两行代码来解决(创建新表,而不仅仅是重命名临时表)
对于SQLite3 c ++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | void GetTableColNames( tstring sTableName , std::vector<tstring> *pvsCols ) { UASSERT(pvsCols); CppSQLite3Table table1; tstring sDML = StringOps::std_sprintf(_T("SELECT * FROM %s") , sTableName.c_str() ); table1 = getTable( StringOps::tstringToUTF8string(sDML).c_str() ); FOR ( INT nCol = 0 ; nCol < table1.numFields() ; nCol++ ) { const CHAR* pch1 = table1.fieldName(nCol); pvsCols->push_back( StringOps::UTF8charTo_tstring(pch1)); } } bool ColExists( tstring sColName ) { bool bColExists = TRUE; try { tstring sQuery = StringOps::std_sprintf(_T("SELECT %s FROM MyOriginalTable LIMIT 1;") , sColName.c_str() ); ShowVerbalMessages(FALSE); CppSQLite3Query q = execQuery( StringOps::tstringTo_stdString(sQuery).c_str() ); ShowVerbalMessages(TRUE); } catch (CppSQLite3Exception& e) { bColExists = FALSE; } RETURN bColExists; } void DeleteColumns( std::vector<tstring> *pvsColsToDelete ) { UASSERT(pvsColsToDelete); execDML( StringOps::tstringTo_stdString(_T("begin transaction;")).c_str() ); std::vector<tstring> vsCols; GetTableColNames( _T("MyOriginalTable") , &vsCols ); CreateFields( _T("TempTable1") , FALSE ); tstring sFieldNamesSeperatedByCommas; FOR ( INT nCol = 0 ; nCol < vsCols.size() ; nCol++ ) { tstring sColNameCurr = vsCols.at(nCol); bool bUseCol = TRUE; FOR ( INT nColsToDelete = 0; nColsToDelete < pvsColsToDelete->SIZE() ; nColsToDelete++ ) { IF ( pvsColsToDelete->at(nColsToDelete) == sColNameCurr ) { bUseCol = FALSE; break; } } IF ( bUseCol ) sFieldNamesSeperatedByCommas+= (sColNameCurr + _T(",")); } IF ( sFieldNamesSeperatedByCommas.at( INT(sFieldNamesSeperatedByCommas.size()) - 1) == _T(',')) sFieldNamesSeperatedByCommas.erase( INT(sFieldNamesSeperatedByCommas.size()) - 1 ); tstring sDML; sDML = StringOps::std_sprintf(_T("insert into TempTable1 SELECT %s FROM MyOriginalTable;\ ") , sFieldNamesSeperatedByCommas.c_str() ); execDML( StringOps::tstringTo_stdString(sDML).c_str() ); sDML = StringOps::std_sprintf(_T("ALTER TABLE MyOriginalTable RENAME TO MyOriginalTable_old\ ") ); execDML( StringOps::tstringTo_stdString(sDML).c_str() ); sDML = StringOps::std_sprintf(_T("ALTER TABLE TempTable1 RENAME TO MyOriginalTable\ ") ); execDML( StringOps::tstringTo_stdString(sDML).c_str() ); sDML = ( _T("DROP TABLE MyOriginalTable_old;") ); execDML( StringOps::tstringTo_stdString(sDML).c_str() ); execDML( StringOps::tstringTo_stdString(_T("commit transaction;")).c_str() ); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | PRAGMA foreign_keys=off; BEGIN TRANSACTION; ALTER TABLE table1 RENAME TO _table1_old; CREATE TABLE table1 ( ( column1 datatype [ NULL | NOT NULL ], column2 datatype [ NULL | NOT NULL ], ... ); INSERT INTO table1 (column1, column2, ... column_n) SELECT column1, column2, ... column_n FROM _table1_old; COMMIT; PRAGMA foreign_keys=ON; |
有关更多信息:
https://www.techonthenet.com/sqlite/tables/alter_table.php
以防万一它可以帮助像我这样的人。
基于官方网站和"已接受"的答案,我使用C#编写了一个代码,该代码使用了System.Data.SQLite NuGet包。
此代码还保留了主键和外键。
C#中的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | void RemoveColumnFromSqlite (string tableName, string columnToRemove) { try { var mSqliteDbConnection = NEW SQLiteConnection ("Data Source=db_folder\\\\MySqliteBasedApp.db;Version=3;Page Size=1024;"); mSqliteDbConnection.Open (); // Reads ALL COLUMNS definitions FROM TABLE List<string> columnDefinition = NEW List<string> (); var mSql = $"SELECT type, sql FROM sqlite_master WHERE tbl_name='{tableName}'"; var mSqliteCommand = NEW SQLiteCommand (mSql, mSqliteDbConnection); string sqlScript =""; USING (mSqliteReader = mSqliteCommand.ExecuteReader ()) { while (mSqliteReader.Read ()) { sqlScript = mSqliteReader["sql"].ToString (); break; } } IF (!string.IsNullOrEmpty (sqlScript)) { // Gets string WITHIN FIRST '(' AND LAST ')' characters INT firstIndex = sqlScript.IndexOf ("("); INT lastIndex = sqlScript.LastIndexOf (")"); IF (firstIndex >= 0 && lastIndex <= sqlScript.Length - 1) { sqlScript = sqlScript.Substring (firstIndex, lastIndex - firstIndex + 1); } string[] scriptParts = sqlScript.Split (NEW string[] {"," }, StringSplitOptions.RemoveEmptyEntries); foreach (string s IN scriptParts) { IF (!s.Contains (columnToRemove)) { columnDefinition.Add (s); } } } string columnDefinitionString = string.Join (",", columnDefinition); // Reads ALL COLUMNS FROM TABLE List<string> COLUMNS = NEW List<string> (); mSql = $"PRAGMA table_info({tableName})"; mSqliteCommand = NEW SQLiteCommand (mSql, mSqliteDbConnection); USING (mSqliteReader = mSqliteCommand.ExecuteReader ()) { while (mSqliteReader.Read ()) COLUMNS.Add (mSqliteReader["name"].ToString ()); } COLUMNS.Remove (columnToRemove); string columnString = string.Join (",", COLUMNS); mSql ="PRAGMA foreign_keys=OFF"; mSqliteCommand = NEW SQLiteCommand (mSql, mSqliteDbConnection); INT n = mSqliteCommand.ExecuteNonQuery (); // Removes a COLUMN FROM the TABLE USING (SQLiteTransaction tr = mSqliteDbConnection.BeginTransaction ()) { USING (SQLiteCommand cmd = mSqliteDbConnection.CreateCommand ()) { cmd.Transaction = tr; string query = $"CREATE TEMPORARY TABLE {tableName}_backup {columnDefinitionString}"; cmd.CommandText = query; cmd.ExecuteNonQuery (); cmd.CommandText = $"INSERT INTO {tableName}_backup SELECT {columnString} FROM {tableName}"; cmd.ExecuteNonQuery (); cmd.CommandText = $"DROP TABLE {tableName}"; cmd.ExecuteNonQuery (); cmd.CommandText = $"CREATE TABLE {tableName} {columnDefinitionString}"; cmd.ExecuteNonQuery (); cmd.CommandText = $"INSERT INTO {tableName} SELECT {columnString} FROM {tableName}_backup;"; cmd.ExecuteNonQuery (); cmd.CommandText = $"DROP TABLE {tableName}_backup"; cmd.ExecuteNonQuery (); } tr.Commit (); } mSql ="PRAGMA foreign_keys=ON"; mSqliteCommand = NEW SQLiteCommand (mSql, mSqliteDbConnection); n = mSqliteCommand.ExecuteNonQuery (); } catch (Exception ex) { HandleExceptions (ex); } } |
如果有人需要(几乎)立即可用的PHP函数,则基于此答案的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /** * Remove a column from a table. * * @param string $tableName The table to remove the column from. * @param string $columnName The column to remove from the table. */ public FUNCTION DropTableColumn($tableName, $columnName) { // -- // Determine ALL COLUMNS EXCEPT the one TO remove. $columnNames = array(); $statement = $pdo->PREPARE("PRAGMA table_info($tableName);"); $statement->EXECUTE(array()); $rows = $statement->fetchAll(PDO::FETCH_OBJ); $hasColumn = FALSE; foreach ($rows AS $row) { IF(strtolower($row->name) !== strtolower($columnName)) { array_push($columnNames, $row->name); } ELSE { $hasColumn = TRUE; } } // COLUMN does NOT exist IN TABLE, no need TO do anything. IF ( !$hasColumn ) RETURN; // -- // Actually EXECUTE the SQL. $columns = implode('`,`', $columnNames); $statement = $pdo->EXEC( "CREATE TABLE `t1_backup` AS SELECT `$columns` FROM `$tableName`; DROP TABLE `$tableName`; ALTER TABLE `t1_backup` RENAME TO `$tableName`;"); } |
与其他答案相反,此方法中使用的SQL似乎保留了列的数据类型,而类似的答案似乎导致所有列的类型均为
更新1:
使用的SQL的缺点是不会保留