关于sql:从SQLite表中删除列

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似乎保留了列的数据类型,而类似的答案似乎导致所有列的类型均为TEXT

    更新1:

    使用的SQL的缺点是不会保留autoincrement列。