Hapus kolom dari tabel SQLite


114

Saya punya masalah: Saya perlu menghapus kolom dari database SQLite saya. Saya menulis kueri ini

alter table table_name drop column column_name 

tapi tidak berhasil. Tolong bantu aku.

Jawaban:


207

Dari: http://www.sqlite.org/faq.html :

(11) Bagaimana cara menambah atau menghapus kolom dari tabel yang ada di SQLite.

SQLite memiliki dukungan ALTER TABLE terbatas yang dapat Anda gunakan untuk menambahkan kolom ke akhir tabel atau untuk mengubah nama tabel. Jika Anda ingin membuat perubahan yang lebih kompleks dalam struktur tabel, Anda harus membuat ulang tabel tersebut. Anda dapat menyimpan data yang ada ke tabel sementara, melepaskan tabel lama, membuat tabel baru, lalu menyalin kembali data dari tabel sementara.

Misalnya, Anda memiliki tabel bernama "t1" dengan nama kolom "a", "b", dan "c" dan Anda ingin menghapus kolom "c" dari tabel ini. Langkah-langkah berikut menggambarkan bagaimana ini dapat dilakukan:

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
+ Selalu baca dokumentasi SQLite. Anda akan melihat terlalu banyak batasan dan perbedaan dalam tata bahasa SQL saat Anda mendapatkan kesalahan. Dokumentasi SQLite sangat mudah dipahami. Jangan khawatir tentang itu.
AhmetB - Google

2
Anda perlu melakukan perintah VACUUM setelah menghapus kolom untuk keamanan; tanpa vakum, file database masih berisi data kolom yang dihapus.
jj1bdx

@ jj1bdx Saya rasa ini tidak masih berisi data, tetapi "ruang disk yang tidak terpakai ditambahkan ke" daftar bebas "internal dan digunakan kembali saat Anda memasukkan data lagi. Ruang disk tidak hilang. Tapi juga tidak kembali ke sistem operasi. " seperti dikutip dari situs sqlite3.
Guilherme Salomé

Seperti yang saya digunakan beberapa kepindahan kolom dalam satu transaksi ini bekerja hanya ketika saya dihapus TEMPORARYdari CREATE TABLE.
ephemerr

Ada implementasi saya menggunakan QSqlQuery Qt: gist.github.com/ephemerr/568d0d41bc389ec78f9fb7d1f015a82a
ephemerr

56

Alih-alih menghapus tabel cadangan, cukup ganti namanya ...

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
Ini tidak akan berfungsi jika Anda memiliki kunci awal yang terhubung ke t1.
ephemerr

39

Untuk kesederhanaan, mengapa tidak membuat tabel cadangan dari pernyataan pilih?

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

3
Pendekatan ini tampaknya mempertahankan tipe data kolom, sedangkan sesuatu seperti jawaban yang diterima tampaknya menghasilkan semua kolom menjadi tipe TEXT.
Uwe Keim

2
Pernyataan ini juga harus dibungkus dalam transaksi.
Georg Schölly

10
Perhatikan bahwa ini tidak mempertahankan kunci utama dan sqlite tidak mendukung mengubah tabel untuk menambahkan kunci utama. Jadi jika kunci utama penting, ini bukan yang harus Anda gunakan
Tim

2
Ini juga tidak mempertahankan NOT NULL.
FutureShocked

jawaban yang diterima berfungsi dengan baik. Anda diharapkan untuk menentukan tipe data saat Anda membuat tabel. Mendesah.
John Lord

8

Opsi ini hanya berfungsi jika Anda dapat membuka DB di Browser DB seperti Browser DB untuk SQLite .

Di Browser DB untuk SQLite:

  1. Buka tab, "Struktur Database"
  2. Pilih tabel Anda Pilih Ubah tabel (tepat di bawah tab)
  3. Pilih kolom yang ingin Anda hapus
  4. Klik pada kolom Hapus dan klik OK

3

=> Buat tabel baru langsung dengan query berikut:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> Sekarang masukkan data ke dalam nama_tabel dari tabel_yang ada dengan kueri berikut:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> Sekarang jatuhkan tabel_yang ada dengan kueri berikut:

DROP TABLE existing_table;

1

Untuk 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

Saya telah membuat fungsi Python di mana Anda memasukkan tabel dan kolom untuk dihapus sebagai argumen:

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

Sesuai info tentang jawaban Duda dan MeBigFatGuy, ini tidak akan berfungsi jika ada kunci asing di atas meja, tetapi ini dapat diperbaiki dengan 2 baris kode (membuat tabel baru dan tidak hanya mengganti nama tabel sementara)


Apa c? Apa itu samb? Jawaban ini membuat terlalu banyak asumsi tentang variabel yang tersedia yang jenisnya tidak diketahui.
Ivan Castellanos

0

Jika ada yang membutuhkan fungsi PHP (hampir) siap pakai, berikut ini didasarkan pada jawaban ini :

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

Berbeda dengan jawaban lain, SQL yang digunakan dalam pendekatan ini tampaknya mempertahankan tipe data kolom, sedangkan sesuatu seperti jawaban yang diterima tampaknya menghasilkan semua kolom menjadi tipe TEXT.

Pembaruan 1:

SQL yang digunakan memiliki kekurangan yaitu autoincrementkolom tidak dipertahankan.


0

Untuk berjaga-jaga jika itu bisa membantu seseorang seperti saya.

Berdasarkan situs resmi dan jawaban yang diterima , saya membuat kode menggunakan C # yang menggunakan paket System.Data.SQLite NuGet.

Kode ini juga mempertahankan kunci Primer dan kunci Asing .

KODE di 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;

Untuk info lebih lanjut: https://www.techonthenet.com/sqlite/tables/alter_table.php

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.