Xóa cột khỏi bảng SQLite


114

Tôi gặp sự cố: Tôi cần xóa một cột khỏi cơ sở dữ liệu SQLite của mình. Tôi đã viết truy vấn này

alter table table_name drop column column_name 

Nhưng nó không hoạt động. Làm ơn giúp tôi.

Câu trả lời:


207

Từ: http://www.sqlite.org/faq.html :

(11) Làm cách nào để thêm hoặc xóa các cột khỏi bảng hiện có trong SQLite.

SQLite có hỗ trợ ALTER TABLE giới hạn mà bạn có thể sử dụng để thêm một cột vào cuối bảng hoặc để thay đổi tên của bảng. Nếu bạn muốn thực hiện những thay đổi phức tạp hơn trong cấu trúc của bảng, bạn sẽ phải tạo lại bảng. Bạn có thể lưu dữ liệu hiện có vào bảng tạm thời, xóa bảng cũ, tạo bảng mới, sau đó sao chép dữ liệu trở lại từ bảng tạm thời.

Ví dụ: giả sử bạn có một bảng có tên "t1" với các tên cột "a", "b" và "c" và bạn muốn xóa cột "c" khỏi bảng này. Các bước sau minh họa cách thực hiện điều này:

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;

8
+ Luôn đọc tài liệu SQLite. Bạn sẽ nhận thấy quá nhiều hạn chế và khác biệt trong ngữ pháp SQL khi bạn gặp lỗi. Tài liệu SQLite rất dễ hiểu. Đừng lo lắng về nó.
AhmetB - Google

2
Bạn cần thực hiện lệnh VACUUM sau khi loại bỏ các cột để bảo mật; không hút bụi, tệp cơ sở dữ liệu vẫn chứa dữ liệu của các cột đã bị xóa.
jj1bdx 14/02/17

@ jj1bdx Tôi không nghĩ rằng nó vẫn chứa dữ liệu, nhưng "dung lượng đĩa không sử dụng được thêm vào" danh sách miễn phí "nội bộ và được sử dụng lại vào lần sau khi bạn chèn dữ liệu. Dung lượng đĩa không bị mất. Nhưng cũng không phải vậy đã trở lại hệ điều hành. " như được trích dẫn từ trang web sqlite3.
Guilherme Salomé

Vì tôi đã sử dụng xóa nhiều cột trong một giao dịch, điều này chỉ hoạt động khi tôi xóa TEMPORARYkhỏi CREATE TABLE.
ephemerr

Có cách triển khai của tôi bằng QSqlQuery của Qt: gist.github.com/ephemerr/568d0d41bc389ec78f9fb7d1f015a82a
ephemerr

56

Thay vì bỏ bảng sao lưu, chỉ cần đổi tên nó ...

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;

6
Nó sẽ không hoạt động khi bạn đã kết nối khóa foregin t1.
ephemerr

39

Để đơn giản, tại sao không tạo bảng sao lưu từ câu lệnh select?

CREATE TABLE t1_backup AS SELECT a, b FROM t1;
DROP TABLE t1;
ALTER TABLE t1_backup RENAME TO t1;

3
Cách tiếp cận này dường như bảo toàn kiểu dữ liệu của các cột, trong khi một thứ gì đó giống như câu trả lời được chấp nhận dường như dẫn đến tất cả các cột đều có kiểu TEXT.
Uwe Keim

2
Các câu lệnh này cũng nên được gói gọn trong một giao dịch.
Georg Schölly

10
Lưu ý rằng điều này không bảo toàn khóa chính và sqlite không hỗ trợ thay đổi bảng để thêm khóa chính. Vì vậy, nếu khóa chính quan trọng, thì đây không phải là thứ bạn nên sử dụng
Tim

2
Điều này cũng không bảo toàn NOT NULL.
FutureShocked

câu trả lời được chấp nhận hoạt động tốt. Bạn phải chỉ định các kiểu dữ liệu khi tạo bảng. Thở dài.
John Lord

8

Tùy chọn này chỉ hoạt động nếu bạn có thể mở DB trong một Trình duyệt DB như Trình duyệt DB cho SQLite .

Trong Trình duyệt DB cho SQLite:

  1. Đi tới tab, "Cấu trúc cơ sở dữ liệu"
  2. Chọn bảng của bạn Chọn Sửa đổi bảng (ngay dưới các tab)
  3. Chọn cột bạn muốn xóa
  4. Bấm vào trường Xóa và bấm OK

3

=> Tạo một bảng mới trực tiếp với truy vấn sau:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> Bây giờ hãy chèn dữ liệu vào table_name từ current_table với truy vấn sau:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> Bây giờ hãy thả hiện_bạn bằng cách truy vấn sau:

DROP TABLE existing_table;

1

Đối với SQLite3 c ++:

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;\n") , sFieldNamesSeperatedByCommas.c_str() );
    execDML( StringOps::tstringTo_stdString(sDML).c_str() );

    sDML = StringOps::std_sprintf(_T("ALTER TABLE MyOriginalTable RENAME TO MyOriginalTable_old\n") );
    execDML( StringOps::tstringTo_stdString(sDML).c_str() );

    sDML = StringOps::std_sprintf(_T("ALTER TABLE TempTable1 RENAME TO MyOriginalTable\n") );
    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

Tôi đã tạo một hàm Python trong đó bạn nhập bảng và cột để xóa dưới dạng đối số:

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()

Theo thông tin về câu trả lời của Duda và MeBigFatGuy, điều này sẽ không hoạt động nếu có khóa ngoại trên bảng, nhưng điều này có thể được khắc phục bằng 2 dòng mã (tạo một bảng mới chứ không chỉ đổi tên bảng tạm thời)


C là gì? Conn là gì? Câu trả lời này tạo ra quá nhiều giả định về các biến sẵn có thuộc loại không xác định.
Ivan Castellanos

0

Trong trường hợp bất kỳ ai cần một hàm PHP (gần như) sẵn sàng để sử dụng, thì câu trả lời dựa trên câu trả lời sau:

/**
 * 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`;");
}

Ngược lại với các câu trả lời khác, SQL được sử dụng trong cách tiếp cận này dường như bảo toàn kiểu dữ liệu của các cột, trong khi một thứ gì đó giống như câu trả lời được chấp nhận dường như dẫn đến tất cả các cột đều có kiểu TEXT.

Cập nhật 1:

SQL được sử dụng có nhược điểm là autoincrementcác cột không được bảo toàn.


0

Chỉ trong trường hợp nếu nó có thể giúp một người như tôi.

Dựa trên trang web Chính thứccâu trả lời Được chấp nhận , tôi đã tạo mã bằng C # sử dụng gói System.Data.SQLite NuGet.

Mã này cũng bảo tồn khóa chínhkhóa ngoại .

MÃ trong C #:

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);
 }
}

0
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;

Để biết thêm thông tin: https://www.techonthenet.com/sqlite/tables/alter_table.php

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.