SQLITE'de sütun nasıl silinir veya eklenir?


240

Sqlite veritabanına sütun silmek veya eklemek istiyorum

Sütunu silmek için aşağıdaki sorguyu kullanıyorum.

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

Ama hata veriyor

System.Data.SQLite.SQLiteException: SQLite error
near "DROP": syntax error

Yanıtlar:


353

ALTER TABLO SQLite

SQLite sınırlı bir ALTER TABLE alt kümesini destekler. SQLite'deki ALTER TABLE komutu, kullanıcının bir tabloyu yeniden adlandırmasına veya var olan bir tabloya yeni bir sütun eklemesine olanak tanır. Bir sütunu yeniden adlandırmak, bir sütunu kaldırmak veya tabloya kısıtlama eklemek veya kaldırmak mümkün değildir.

Yapabilirsin:

  1. değiştirmeye çalıştığınız masa olarak yeni tablo oluşturun,
  2. tüm verileri kopyala,
  3. eski masayı düşür,
  4. yenisini yeniden adlandırın.

47
stackoverflow.com/a/5987838/1578528 görevi gerçekleştirmek için temel bir örnek verir.
bikram990

4
Bu sekansı yapmadan önce ve bu tabloya atıfta bulunan harici tabloların olduğu durumlarda, çağrı yapılmalıdır PRAGMA foreign_keys=OFF. Bu durumda, bu diziyi yaptıktan sonra PRAGMA foreign_keys=ONyabancı anahtarları yeniden etkinleştirmek için aramalısınız .
PazO

Freoign anahtarını ve dizinleri de nasıl kopyalarsınız?
Jonathan

seçimden bir oluşturma yerine önce yeni bir tablo oluşturduğunuz sürece, tüm bu şeylere sahip olacaktır.
John Lord

1
Daha yeni SQLite sürümlerinde RENAME COLUMNdesteklenir. L sqlite.org/releaselog/3_25_0.html
Grogs

46

Bunu yapmak için Sqlite'ın önerilen yolunu temel alan bir Java uygulaması yazdım:

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

Tablonun sütun almak için "PRAGMA table_info" kullandım:

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

Aslında blogumda yazdım, orada daha fazla açıklama görebilirsiniz:

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


1
bu oldukça yavaş olsa da değil mi? büyük veri tabloları için?
Joran Beasley

2
Bunun, başka bir kodun geçiş durumundaki şeyleri görmesine izin vermek yerine, tek bir işlemle yapılması daha iyi olurdu.
Donal Fellows

Bu kod i genellikle diğer kod aynı anda çalışmıyor DB yükseltme yaparken çalıştırın. Bir işlem oluşturabilir ve içindeki tüm bu komutları yapabilirsiniz.
Udinic

3
Bu çözümü kullanırsanız, sonuç tablonuzdaki sütunların tamamen çıplak olacağından eminim - tür bilgisi, PK, FK, varsayılan değerler, benzersiz veya kontrol kısıtlamaları kalmayacak. Yeni tabloya aktardığı tek şey sütun adıdır. Ayrıca, çalıştırmadan önce yabancı anahtarları devre dışı bırakmadığından, diğer tablolardaki veriler de bozulabilir.
ACK_stoverflow

4
Alternatif olarak, bir INSERTifade yapmak yerine , yeni tabloyu aşağıdakileri yaparak da oluşturabilirsiniz:"CREAT TABLE" + tableName + "AS SELECT " + columnsSeperated + " FROM " + tableName + "_old;"
Robert

26

Diğerlerinin işaret ettiği gibi

Bir sütunu yeniden adlandırmak, bir sütunu kaldırmak veya tabloya kısıtlama eklemek veya kaldırmak mümkün değildir.

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

Her zaman yeni bir tablo oluşturabilir ve sonra eski tabloyu bırakabilirsiniz. Bu geçici çözümü bir örnekle açıklamaya çalışacağım .

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       

Şimdi sütunu heightbu tablodan kaldırmak istiyorsunuz .

Adlı başka bir tablo oluştur new_person

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

Şimdi verileri eski tablodan kopyalayın

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>

Şimdi Bırak personmasa ve yeniden adlandırma new_personiçinperson

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

Yani şimdi bir .schemayaparsan, göreceksin

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

Yabancı referanslar ne olacak? Oracle, başka bir tablonun kullandığı bir tabloyu silerseniz şikayet eder.
Leos Literak

6
Gerçek bir programcı olduğunuzu söyleyebilirim. John
Doe'dan

1
CREATE TABLE new_person AS SELECT id, first_name, last_name, age FROM person;
Clay



4

Diğerleri işaret gibi, SQLite'nın ALTER TABLEifadesi yok değil desteklemek DROP COLUMNve bunu yapmak için standart reçete kısıtlamaları ve indeksleri korumaz.

İşte tüm temel kısıtlamaları ve endeksleri korurken bunu genel olarak yapmak için bazı python kodu .

Lütfen kullanmadan önce veritabanınızı yedekleyin!Bu işlev, orijinal CREATE TABLE ifadesine dokundurmaya dayanır ve potansiyel olarak biraz güvensizdir - örneğin, bir tanımlayıcı gömülü virgül veya parantez içeriyorsa yanlış bir şey yapar.

Herkes SQL ayrıştırmak için daha iyi bir yol katkıda isterdi, bu harika olurdu!

GÜNCELLEME Açık kaynaksqlparsepaketinikullanarak ayrıştırmanın daha iyi bir yolunu buldum. Burada yayınlayacağım herhangi bir ilgi varsa, sadece bir yorum bırakın ...

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

Masaya yabancı anahtarlar da veriyor mu?
Lasse V. Karlsen

@ LasseV.Karlsen Bazı testler yaptım ve bunlar tablo ismiyle zorlanmış gibi göründüğü için yabancı anahtar kısıtlamalarını korumalı.
spam_eggs

Java'dan nasıl çalıştırabilirim?
Leos Literak

4

Kod otomatik olarak tablo oluşturma sorgusu üretir böylece @Udinic cevap yeniden yazdı . Ayrıca gerek yok . Ayrıca bunu bir işlem içinde yapmak zorundadır .ConnectionSource

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


}

3

SQLite için DB Tarayıcı sütun eklemenize veya bırakmanıza izin verir.

Ana görünümde sekmesinde Database Structuretablo adını tıklayın. Modify TableSütunu / alanı seçip kaldırabileceğiniz yeni bir pencere açan bir düğme etkinleştirilir.


2

Sqlitebrowser kullanabilirsiniz. Tarayıcı modunda, ilgili veritabanı ve tablo için, -tatabase yapısı sekmesi altında, Tablo Değiştir seçeneğini izleyerek ilgili sütun kaldırılabilir.


2

User2638929 yanıtını geliştirdim ve şimdi sütun türünü, birincil anahtarı, varsayılan değeri vb.

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. Burada kullandım android.arch.persistence.db.SupportSQLiteDatabase, ancak kullanım için kolayca değiştirebilirsinizandroid.database.sqlite.SQLiteDatabase


2

Sanırım ne yapmak istediğiniz veritabanı geçişidir. Bir sütunu 'bırakma' SQLite'de mevcut değildir. Ancak, ALTER tablo sorgusunu kullanarak fazladan bir sütun ekleyebilirsiniz.


1

Sütun adlarını değiştirmek için SQlite Yöneticisi'ni kullanabilirsiniz. Tablo adına sağ tıklayın ve Tabloyu Düzenle'yi seçin. Burada tablo yapısını bulacaksınız ve kolayca yeniden adlandırabilirsiniz.



1

Alternatif olarak:

Şema içeren bir tablonuz varsa

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

CREATE TABLE...ASgibi bir ifade kullanabilirsiniz CREATE TABLE person2 AS SELECT id, first_name, last_name, age FROM person;, yani istemediğiniz sütunları dışarıda bırakın. Ardından orijinal persontabloyu bırakın ve yenisini yeniden adlandırın.

Bu yöntem bir tablo hiçbir PRIMARY KEY ve hiçbir kısıtlamaları üretir unutmayın. Bunları korumak için, yeni bir tablo oluşturmak için başkalarının açıkladığı yöntemleri kullanın veya geçici bir tabloyu ara olarak kullanın .


1

Farklı bir soruya verilen bu yanıt bir sütunu değiştirmeye yöneliktir , ancak çok sayıda sütununuz varsa ve INSERT ifadeniz için çoğunu elle yeniden yazmak istemiyorsanız, cevabın bir bölümünün de yararlı bir yaklaşım sağlayabileceğine inanıyorum:

https://stackoverflow.com/a/10385666

Veritabanınızı yukarıdaki bağlantıda açıklandığı gibi dökebilir, ardından "döküm oluştur" ifadesini ve bu dökümden "ekle" şablonunu alabilir, ardından SQLite SSS girişindeki "Mevcut bir sütunu nasıl ekleyebilir veya silebilirim?" SQLite tablosunda. " (SSS bu sayfanın başka bir yerine bağlanmıştır.)


Aslında, dökümün varsayılan olarak ekte sütun adları içermediğini fark ettim. Bu nedenle, sütun adlarını almak için .schema pragma'yı kullanmak aynı derecede iyi olabilir, çünkü daha sonra tür bildirimlerini her iki şekilde de silmeniz gerekir.
burpgrass

1

Http://www.sqlite.org/faq.html#q11 adresindekiPython bilgilere dayalı olarak uygulama .

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)

Bu komut dosyası önce rasgele geçici tablo oluşturur ve bırakılacaklar dışında yalnızca gerekli sütunların verilerini ekler. Daha sonra geçici tabloyu temel alarak özgün tabloyu geri yükler ve geçici tabloyu bırakır.


1

Benim çözümüm, sadece bu yöntemi çağırmak gerekiyor.

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

Ve sütunları almak için yardımcı yöntem:

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

Bu yöntem colunm tipini tutmaz, bu yüzden kodunuzun değiştirilmiş versiyonunu yayınladım
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+";");
            }
        }
    }
}

ve sadece bir yöntem çağırın

DeleteColFromTable("Database name","Table name","Col name which want to delete");

-1

Artık sütunları işlemek için SQLite için DB tarayıcıyı da kullanabilirsiniz


-2

sütun eklemek için örnek: -

alter table student add column TOB time;

Burada öğrenci olduğunu tablo_ismi ve TOB olan sütun_ismi eklenecek.

Çalışıyor ve test ediliyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.