Bagaimana cara menghapus atau menambah kolom dalam SQLITE?

240

Saya ingin menghapus atau menambah kolom dalam basis data sqlite

Saya menggunakan kueri berikut untuk menghapus kolom.

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

Tapi itu memberi kesalahan

System.Data.SQLite.SQLiteException: SQLite error
near "DROP": syntax error
Sandip
sumber
1
Kemungkinan duplikat kolom Hapus dari tabel SQLite
Komunitas Ans

Jawaban:

353

ALTER TABLE SQLite

SQLite mendukung subset terbatas dari ALTER TABLE. Perintah ALTER TABLE dalam SQLite memungkinkan pengguna untuk mengubah nama tabel atau menambahkan kolom baru ke tabel yang sudah ada. Tidak mungkin untuk mengganti nama kolom, menghapus kolom, atau menambah atau menghapus batasan dari tabel.

Kamu bisa:

  1. buat tabel baru sebagai yang ingin Anda ubah,
  2. salin semua data,
  3. jatuhkan meja lama,
  4. ganti nama yang baru.
Michał Powaga
sumber
47
stackoverflow.com/a/5987838/1578528 memberikan contoh dasar untuk melakukan tugas.
bikram990
4
Sebelum melakukan urutan ini dan dalam kasus di mana ada tabel eksternal yang mengacu pada tabel ini, seseorang harus memanggil PRAGMA foreign_keys=OFF. Dalam hal ini, setelah melakukan urutan ini, seseorang harus menelepon PRAGMA foreign_keys=ONuntuk mengaktifkan kembali kunci asing.
PazO
Bagaimana Anda juga menyalin kunci bebas dan indeks juga?
Jonathan
selama Anda membuat tabel baru terlebih dahulu alih-alih membuat dari pilih, itu akan memiliki semua hal itu.
John Lord
1
Dalam versi SQLite yang lebih baru, RENAME COLUMNdidukung. 🎉 sqlite.org/releaselog/3_25_0.html
Grogs
46

Saya telah menulis implementasi Java berdasarkan cara yang disarankan Sqlite untuk melakukan ini:

private void dropColumn(SQLiteDatabase db,
        ConnectionSource connectionSource,
        String createTableCmd,
        String tableName,
        String[] colsToRemove) throws java.sql.SQLException {

    List<String> updatedTableColumns = getTableColumns(tableName);
    // Remove the columns we don't want anymore from the table's list of columns
    updatedTableColumns.removeAll(Arrays.asList(colsToRemove));

    String columnsSeperated = TextUtils.join(",", updatedTableColumns);

    db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");

    // Creating the table on its new format (no redundant columns)
    db.execSQL(createTableCmd);

    // Populating the table with the data
    db.execSQL("INSERT INTO " + tableName + "(" + columnsSeperated + ") SELECT "
            + columnsSeperated + " FROM " + tableName + "_old;");
    db.execSQL("DROP TABLE " + tableName + "_old;");
}

Untuk mendapatkan kolom tabel, saya menggunakan "PRAGMA table_info":

public List<String> getTableColumns(String tableName) {
    ArrayList<String> columns = new ArrayList<String>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = getDB().rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}

Saya benar-benar menulis tentang itu di blog saya, Anda dapat melihat lebih banyak penjelasan di sana:

http://udinic.wordpress.com/2012/05/09/sqlite-drop-column-support/

Udinic
sumber
1
ini cukup lambat bukan? untuk tabel data besar?
Joran Beasley
2
Akan lebih baik jika ini dilakukan dalam satu transaksi, daripada berpotensi memungkinkan kode lain untuk melihat sesuatu dalam keadaan transisi.
Donal Fellows
Kode ini biasanya saya jalankan ketika memutakhirkan DB, di mana kode lain tidak berjalan secara bersamaan. Anda dapat membuat transaksi dan melakukan semua perintah di dalamnya.
Udinic
3
Saya cukup yakin bahwa jika Anda menggunakan solusi ini kolom pada tabel hasil Anda akan sepenuhnya kosong - tidak ada informasi jenis, PK, FK, nilai default, batasan unik atau periksa akan tetap ada. Semua yang diimpor ke tabel baru adalah nama kolom. Selain itu, karena tidak menonaktifkan kunci asing sebelum berjalan, data di tabel lain juga bisa kacau.
ACK_stoverflow
4
Atau, alih-alih melakukan INSERTpernyataan, Anda juga dapat membuat tabel baru dengan melakukan"CREAT TABLE" + tableName + "AS SELECT " + columnsSeperated + " FROM " + tableName + "_old;"
Robert
26

Seperti yang ditunjukkan orang lain

Tidak mungkin untuk mengganti nama kolom, menghapus kolom, atau menambah atau menghapus batasan dari tabel.

sumber: http://www.sqlite.org/lang_altertable.html

Meskipun Anda selalu dapat membuat tabel baru dan kemudian menjatuhkan yang lebih lama. Saya akan mencoba menjelaskan solusi ini dengan sebuah contoh.

sqlite> .schema
CREATE TABLE person(
 id INTEGER PRIMARY KEY, 
 first_name TEXT,
 last_name TEXT, 
 age INTEGER, 
 height INTEGER
);
sqlite> select * from person ; 
id          first_name  last_name   age         height    
----------  ----------  ----------  ----------  ----------
0           john        doe         20          170       
1           foo         bar         25          171       

Sekarang Anda ingin menghapus kolom heightdari tabel ini.

Buat tabel lain yang disebut new_person

sqlite> CREATE TABLE new_person(
   ...>  id INTEGER PRIMARY KEY, 
   ...>  first_name TEXT, 
   ...>  last_name TEXT, 
   ...>  age INTEGER 
   ...> ) ; 
sqlite> 

Sekarang salin data dari tabel lama

sqlite> INSERT INTO new_person
   ...> SELECT id, first_name, last_name, age FROM person ;
sqlite> select * from new_person ;
id          first_name  last_name   age       
----------  ----------  ----------  ----------
0           john        doe         20        
1           foo         bar         25        
sqlite>

Sekarang Drop the persontable dan ganti namanya new_personmenjadiperson

sqlite> DROP TABLE IF EXISTS person ; 
sqlite> ALTER TABLE new_person RENAME TO person ;
sqlite>

Jadi sekarang jika Anda melakukan .schema, Anda akan melihat

sqlite>.schema
CREATE TABLE "person"(
 id INTEGER PRIMARY KEY, 
 first_name TEXT, 
 last_name TEXT, 
 age INTEGER 
);
Tasdik Rahman
sumber
Bagaimana dengan referensi asing? Oracle akan mengeluh jika Anda menghapus tabel yang menggunakan tabel lain.
Leos Literak
6
Saya dapat mengatakan bahwa Anda adalah programmer sejati. Anda kehabisan nama tepat setelah john doe dan langsung pergi ke "foo bar" :)
msouth
1
CREATE TABLE new_person AS SELECT id, first_name, last_name, age FROM person;
Clay
5

Kami tidak dapat menjatuhkan kolom tertentu dalam SQLite 3. Lihat FAQ .

sandeep valapishetty
sumber
4

Seperti yang telah ditunjukkan orang lain, ALTER TABLEpernyataan sqlite tidak mendukung DROP COLUMN, dan resep standar untuk melakukan ini tidak mempertahankan kendala & indeks.

Berikut adalah beberapa kode python untuk melakukan ini secara umum, sambil mempertahankan semua batasan dan indeks utama.

Harap cadangkan basis data Anda sebelum menggunakan! Fungsi ini bergantung pada pernyataan pernyataan CREATE TABLE yang asli dan berpotensi sedikit tidak aman - misalnya akan melakukan hal yang salah jika pengidentifikasi berisi tanda koma atau tanda kurung yang disematkan.

Jika ada yang mau berkontribusi cara yang lebih baik untuk mengurai SQL, itu akan bagus!

PEMBARUAN Saya menemukan cara yang lebih baik untuk menguraikan menggunakan paket open-sourcesqlparse. Jika ada minat saya akan mempostingnya di sini, tinggalkan komentar memintanya ...

import re
import random

def DROP_COLUMN(db, table, column):
    columns = [ c[1] for c in db.execute("PRAGMA table_info(%s)" % table) ]
    columns = [ c for c in columns if c != column ]
    sql = db.execute("SELECT sql from sqlite_master where name = '%s'" 
        % table).fetchone()[0]
    sql = format(sql)
    lines = sql.splitlines()
    findcol = r'\b%s\b' % column
    keeplines = [ line for line in lines if not re.search(findcol, line) ]
    create = '\n'.join(keeplines)
    create = re.sub(r',(\s*\))', r'\1', create)
    temp = 'tmp%d' % random.randint(1e8, 1e9)
    db.execute("ALTER TABLE %(old)s RENAME TO %(new)s" % { 
        'old': table, 'new': temp })
    db.execute(create)
    db.execute("""
        INSERT INTO %(new)s ( %(columns)s ) 
        SELECT %(columns)s FROM %(old)s
    """ % { 
        'old': temp,
        'new': table,
        'columns': ', '.join(columns)
    })  
    db.execute("DROP TABLE %s" % temp)

def format(sql):
    sql = sql.replace(",", ",\n")
    sql = sql.replace("(", "(\n")
    sql = sql.replace(")", "\n)")
    return sql
spam_eggs
sumber
Apakah itu mempertahankan kunci asing ke meja juga?
Lasse V. Karlsen
@ LasseV.Karlsen Saya melakukan beberapa tes dan harus mempertahankan batasan kunci asing karena ini tampaknya ditegakkan dengan nama tabel.
spam_eggs
Bagaimana saya bisa menjalankannya dari Jawa?
Leos Literak
4

Saya menulis ulang jawaban @Udinic sehingga kode menghasilkan kueri pembuatan tabel secara otomatis . Itu juga tidak perlu ConnectionSource. Ini juga harus melakukan ini di dalam transaksi .

public static String getOneTableDbSchema(SQLiteDatabase db, String tableName) {
    Cursor c = db.rawQuery(
            "SELECT * FROM `sqlite_master` WHERE `type` = 'table' AND `name` = '" + tableName + "'", null);
    String result = null;
    if (c.moveToFirst()) {
        result = c.getString(c.getColumnIndex("sql"));
    }
    c.close();
    return result;
}

public List<String> getTableColumns(SQLiteDatabase db, String tableName) {
    ArrayList<String> columns = new ArrayList<>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = db.rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}

private void dropColumn(SQLiteDatabase db, String tableName, String[] columnsToRemove) {
    db.beginTransaction();
    try {
        List<String> columnNamesWithoutRemovedOnes = getTableColumns(db, tableName);
        // Remove the columns we don't want anymore from the table's list of columns
        columnNamesWithoutRemovedOnes.removeAll(Arrays.asList(columnsToRemove));

        String newColumnNamesSeparated = TextUtils.join(" , ", columnNamesWithoutRemovedOnes);
        String sql = getOneTableDbSchema(db, tableName);
        // Extract the SQL query that contains only columns
        String oldColumnsSql = sql.substring(sql.indexOf("(")+1, sql.lastIndexOf(")"));

        db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
        db.execSQL("CREATE TABLE `" + tableName + "` (" + getSqlWithoutRemovedColumns(oldColumnsSql, columnsToRemove)+ ");");
        db.execSQL("INSERT INTO " + tableName + "(" + newColumnNamesSeparated + ") SELECT " + newColumnNamesSeparated + " FROM " + tableName + "_old;");
        db.execSQL("DROP TABLE " + tableName + "_old;");
        db.setTransactionSuccessful();
    } catch {
        //Error in between database transaction 
    } finally {
        db.endTransaction();
    }


}
soshial
sumber
3

DB Browser untuk SQLite memungkinkan Anda untuk menambah atau menjatuhkan kolom.

Di tampilan utama, tab Database Structure, klik pada nama tabel. Sebuah tombol Modify Tablediaktifkan, yang membuka jendela baru di mana Anda dapat memilih kolom / bidang dan menghapusnya.

domba jantan
sumber
2

Anda dapat menggunakan Sqlitebrowser. Dalam mode browser, untuk database dan tabel masing-masing, di bawah struktur tab-database, mengikuti opsi Modify Table, kolom masing-masing dapat dihapus.

iamigham
sumber
2

Saya telah meningkatkan jawaban user2638929 dan sekarang dapat mempertahankan jenis kolom, kunci utama, nilai default dll.

private static void dropColumn(SupportSQLiteDatabase database, String tableName, List<String> columnsToRemove){
    List<String> columnNames = new ArrayList<>();
    List<String> columnNamesWithType = new ArrayList<>();
    List<String> primaryKeys = new ArrayList<>();
    String query = "pragma table_info(" + tableName + ");";
    Cursor cursor = database.query(query);
    while (cursor.moveToNext()){
        String columnName = cursor.getString(cursor.getColumnIndex("name"));

        if (columnsToRemove.contains(columnName)){
            continue;
        }

        String columnType = cursor.getString(cursor.getColumnIndex("type"));
        boolean isNotNull = cursor.getInt(cursor.getColumnIndex("notnull")) == 1;
        boolean isPk = cursor.getInt(cursor.getColumnIndex("pk")) == 1;

        columnNames.add(columnName);
        String tmp = "`" + columnName + "` " + columnType + " ";
        if (isNotNull){
            tmp += " NOT NULL ";
        }

        int defaultValueType = cursor.getType(cursor.getColumnIndex("dflt_value"));
        if (defaultValueType == Cursor.FIELD_TYPE_STRING){
            tmp += " DEFAULT " + "\"" + cursor.getString(cursor.getColumnIndex("dflt_value")) + "\" ";
        }else if(defaultValueType == Cursor.FIELD_TYPE_INTEGER){
            tmp += " DEFAULT " + cursor.getInt(cursor.getColumnIndex("dflt_value")) + " ";
        }else if (defaultValueType == Cursor.FIELD_TYPE_FLOAT){
            tmp += " DEFAULT " + cursor.getFloat(cursor.getColumnIndex("dflt_value")) + " ";
        }
        columnNamesWithType.add(tmp);
        if (isPk){
            primaryKeys.add("`" + columnName + "`");
        }
    }
    cursor.close();

    String columnNamesSeparated = TextUtils.join(", ", columnNames);
    if (primaryKeys.size() > 0){
        columnNamesWithType.add("PRIMARY KEY("+ TextUtils.join(", ", primaryKeys) +")");
    }
    String columnNamesWithTypeSeparated = TextUtils.join(", ", columnNamesWithType);

    database.beginTransaction();
    try {
        database.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
        database.execSQL("CREATE TABLE " + tableName + " (" + columnNamesWithTypeSeparated + ");");
        database.execSQL("INSERT INTO " + tableName + " (" + columnNamesSeparated + ") SELECT "
                + columnNamesSeparated + " FROM " + tableName + "_old;");
        database.execSQL("DROP TABLE " + tableName + "_old;");
        database.setTransactionSuccessful();
    }finally {
        database.endTransaction();
    }
}

PS. Saya digunakan di sini android.arch.persistence.db.SupportSQLiteDatabase, tetapi Anda dapat dengan mudah memodifikasinya untuk digunakanandroid.database.sqlite.SQLiteDatabase

Berdimurat Masaliev
sumber
2

Saya kira yang ingin Anda lakukan adalah migrasi basis data. 'Drop'ping a kolom tidak ada di SQLite. Namun Anda bisa, menambahkan kolom tambahan dengan menggunakan kueri tabel ALTER.

Subha_26
sumber
1

Anda dapat menggunakan Administrator SQlite untuk mengubah nama kolom. Klik kanan pada nama Table dan pilih Edit Table. Di sini Anda akan menemukan struktur tabel dan Anda dapat dengan mudah mengganti nama.

Tushar Baxi
sumber
1

Karena SQLite memiliki dukungan terbatas untuk ALTER TABLE sehingga Anda hanya dapat ADD kolom di akhir tabel ATAU Ganti TABLE_NAME dalam SQLite.

Berikut ini adalah Jawaban Terbaik BAGAIMANA CARA MENGHAPUS KOLOM DARI SQLITE?

kunjungi Hapus kolom dari tabel SQLite

Gaurav Singla
sumber
1

Sebagai alternatif:

Jika Anda memiliki tabel dengan skema

CREATE TABLE person(
  id INTEGER PRIMARY KEY,
  first_name TEXT,
  last_name TEXT,
  age INTEGER,
  height INTEGER
);

Anda dapat menggunakan CREATE TABLE...ASpernyataan seperti CREATE TABLE person2 AS SELECT id, first_name, last_name, age FROM person;, yaitu meninggalkan kolom yang tidak Anda inginkan. Kemudian jatuhkan persontabel asli dan ganti nama yang baru.

Perhatikan metode ini menghasilkan tabel tidak memiliki KUNCI UTAMA dan tidak ada kendala. Untuk melestarikannya, gunakan metode yang dijelaskan orang lain untuk membuat tabel baru, atau gunakan tabel sementara sebagai perantara.

karkarlawawa
sumber
1

Jawaban untuk pertanyaan yang berbeda ini berorientasi pada memodifikasi kolom, tetapi saya yakin sebagian dari jawabannya juga dapat menghasilkan pendekatan yang berguna jika Anda memiliki banyak kolom dan tidak ingin mengetik ulang sebagian besar dari mereka dengan tangan untuk pernyataan INSERT Anda:

https://stackoverflow.com/a/10385666

Anda bisa membuang basis data Anda seperti yang dijelaskan dalam tautan di atas, lalu ambil pernyataan "buat tabel" dan templat "sisipkan" dari dump itu, lalu ikuti petunjuk di entri FAQ SQLite "Bagaimana cara menambah atau menghapus kolom dari yang sudah ada tabel dalam SQLite. " (FAQ terhubung di tempat lain di halaman ini.)

burpgrass
sumber
Sebenarnya, saya baru menyadari bahwa dump tidak menyertakan nama kolom dalam sisipan secara default. Jadi mungkin sama baiknya hanya menggunakan pragma .schema untuk mengambil nama kolom, karena Anda harus menghapus deklarasi tipe.
burpgrass
1

Implementasi Pythonberdasarkan informasi di http://www.sqlite.org/faq.html#q11 .

import sqlite3 as db
import random
import string

QUERY_TEMPLATE_GET_COLUMNS = "PRAGMA table_info(@table_name)"
QUERY_TEMPLATE_DROP_COLUMN = """
  BEGIN TRANSACTION;
  CREATE TEMPORARY TABLE @tmp_table(@columns_to_keep);
  INSERT INTO @tmp_table SELECT @columns_to_keep FROM @table_name;
  DROP TABLE @table_name;
  CREATE TABLE @table_name(@columns_to_keep);
  INSERT INTO @table_name SELECT @columns_to_keep FROM @tmp_table;
  DROP TABLE @tmp_table;
  COMMIT;
"""

def drop_column(db_file, table_name, column_name):
    con = db.connect(db_file)
    QUERY_GET_COLUMNS = QUERY_TEMPLATE_GET_COLUMNS.replace("@table_name", table_name)
    query_res = con.execute(QUERY_GET_COLUMNS).fetchall()
    columns_list_to_keep = [i[1] for i in query_res if i[1] != column_name]
    columns_to_keep = ",".join(columns_list_to_keep)
    tmp_table = "tmp_%s" % "".join(random.sample(string.ascii_lowercase, 10))
    QUERY_DROP_COLUMN = QUERY_TEMPLATE_DROP_COLUMN.replace("@table_name", table_name)\
        .replace("@tmp_table", tmp_table).replace("@columns_to_keep", columns_to_keep)
    con.executescript(QUERY_DROP_COLUMN)
    con.close()

drop_column(DB_FILE, TABLE_NAME, COLUMN_NAME)

Skrip ini pertama-tama membuat tabel sementara acak dan menyisipkan data hanya kolom yang diperlukan kecuali yang akan dihapus. Kemudian mengembalikan tabel asli berdasarkan tabel sementara dan menjatuhkan tabel sementara.

Akif
sumber
1

Solusi saya, hanya perlu memanggil metode ini.

public static void dropColumn(SQLiteDatabase db, String tableName, String[] columnsToRemove) throws java.sql.SQLException {
    List<String> updatedTableColumns = getTableColumns(db, tableName);
    updatedTableColumns.removeAll(Arrays.asList(columnsToRemove));
    String columnsSeperated = TextUtils.join(",", updatedTableColumns);

    db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
    db.execSQL("CREATE TABLE " + tableName + " (" + columnsSeperated + ");");
    db.execSQL("INSERT INTO " + tableName + "(" + columnsSeperated + ") SELECT "
            + columnsSeperated + " FROM " + tableName + "_old;");
    db.execSQL("DROP TABLE " + tableName + "_old;");
}

Dan metode bantu untuk mendapatkan kolom:

public static List<String> getTableColumns(SQLiteDatabase db, String tableName) {
    ArrayList<String> columns = new ArrayList<>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = db.rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}
pengguna2638929
sumber
Metode ini tidak menyimpan tipe kolom, jadi saya memposting versi modifikasi dari kode Anda
Berdimurat Masaliev
0
public void DeleteColFromTable(String DbName, String TableName, String ColName){
    SQLiteDatabase db = openOrCreateDatabase(""+DbName+"", Context.MODE_PRIVATE, null);
    db.execSQL("CREATE TABLE IF NOT EXISTS "+TableName+"(1x00dff);");
    Cursor c = db.rawQuery("PRAGMA table_info("+TableName+")", null);
    if (c.getCount() == 0) {

    } else {
        String columns1 = "";
        String columns2 = "";
        while (c.moveToNext()) {
            if (c.getString(1).equals(ColName)) {
            } else {
                columns1 = columns1 + ", " + c.getString(1) + " " + c.getString(2);
                columns2 = columns2 + ", " + c.getString(1);
            }
            if (c.isLast()) {
                db.execSQL("CREATE TABLE IF NOT EXISTS DataBackup (" + columns1 + ");");
                db.execSQL("INSERT INTO DataBackup SELECT " + columns2 + " FROM "+TableName+";");
                db.execSQL("DROP TABLE "+TableName+"");
                db.execSQL("ALTER TABLE DataBackup RENAME TO "+TableName+";");
            }
        }
    }
}

dan panggil saja metode

DeleteColFromTable("Database name","Table name","Col name which want to delete");
Sagar Makhija
sumber
-1

Anda sekarang juga dapat menggunakan browser DB untuk SQLite untuk memanipulasi kolom

Jamal Mahroof
sumber
-2

contoh untuk menambahkan kolom: -

alter table student add column TOB time;

di sini siswa adalah nama_kabel dan TOB adalah nama_kolom yang akan ditambahkan.

Ini bekerja dan diuji.

Daredevil Kallol
sumber