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.

berpasir
sumber

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;
MeBigFatGuy
sumber
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;
Duda
sumber
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;
pengguna4086833
sumber
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
MagTun
sumber
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;
pengguna3317939
sumber
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() );   
}
Cerah127
sumber
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)

Dom
sumber
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.

Uwe Keim
sumber
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);
 }
}
Naveen Kumar V
sumber
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

Nexus242
sumber