Come eliminare o aggiungere colonne in SQLITE?


240

Voglio eliminare o aggiungere colonne nel database sqlite

Sto usando la seguente query per eliminare la colonna.

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

Ma dà errore

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

Risposte:


353

ALTER TABLE SQLite

SQLite supporta un sottoinsieme limitato di ALTER TABLE. Il comando ALTER TABLE in SQLite consente all'utente di rinominare una tabella o di aggiungere una nuova colonna a una tabella esistente. Non è possibile rinominare una colonna, rimuovere una colonna o aggiungere o rimuovere vincoli da una tabella.

Puoi:

  1. crea una nuova tabella come quella che stai cercando di cambiare,
  2. copia tutti i dati,
  3. lascia cadere il vecchio tavolo,
  4. rinominare quello nuovo.

47
stackoverflow.com/a/5987838/1578528 fornisce un esempio di base per l'esecuzione dell'attività.
bikram990

4
Prima di eseguire questa sequenza e nei casi in cui vi sono tabelle esterne che fanno riferimento a questa tabella, è necessario chiamare PRAGMA foreign_keys=OFF. In questo caso, dopo aver eseguito questa sequenza, è necessario chiamare PRAGMA foreign_keys=ONper riattivare le chiavi esterne.
PazO,

Come si copiano anche la chiave e gli indici di freoign?
Jonathan,

fintanto che fai prima una nuova tabella anziché una creazione dalla selezione, avrà tutte quelle cose.
John Lord,

1
Nelle versioni più recenti di SQLite, RENAME COLUMNè supportato. 🎉 sqlite.org/releaselog/3_25_0.html
Grogs

46

Ho scritto un'implementazione Java basata sul modo consigliato da Sqlite per fare questo:

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

Per ottenere la colonna della tabella, ho usato "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;
}

In realtà ne ho scritto sul mio blog, puoi vedere più spiegazioni lì:

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


1
questo è piuttosto lento, no? per tabelle di big data?
Joran Beasley,

2
Sarebbe meglio se ciò avvenisse in una singola transazione, piuttosto che consentire potenzialmente ad altro codice di vedere le cose in uno stato di transizione.
Donal Fellows

Questo codice di solito eseguo quando aggiorno il DB, dove l'altro codice non è in esecuzione contemporaneamente. Puoi creare una transazione ed eseguire tutti quei comandi in essa contenuti.
Udinic,

3
Sono abbastanza sicuro che se usi questa soluzione le colonne nella tabella dei risultati saranno completamente nude - non rimarranno informazioni sul tipo, PK, FK, valori predefiniti, vincoli univoci o di controllo. Tutto ciò che importa nella nuova tabella è il nome della colonna. Inoltre, poiché non disabilita le chiavi esterne prima dell'esecuzione, anche i dati in altre tabelle potrebbero essere rovinati.
ACK_stoverflow

4
In alternativa, invece di fare una INSERTdichiarazione, puoi anche creare la nuova tabella facendo un"CREAT TABLE" + tableName + "AS SELECT " + columnsSeperated + " FROM " + tableName + "_old;"
Robert

26

Come altri hanno sottolineato

Non è possibile rinominare una colonna, rimuovere una colonna o aggiungere o rimuovere vincoli da una tabella.

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

Mentre puoi sempre creare una nuova tabella e quindi eliminare quella precedente. Proverò a spiegare questa soluzione alternativa con un esempio.

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       

Ora vuoi rimuovere la colonna heightda questa tabella.

Crea un'altra tabella chiamata new_person

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

Ora copia i dati dalla vecchia tabella

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>

Ora rilascia la persontabella e rinomina new_personinperson

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

Quindi ora se fai un .schema, vedrai

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

E i riferimenti stranieri? Oracle si lamenterebbe se si eliminasse una tabella utilizzata da un'altra tabella.
Leos Literak,

6
Posso dire che sei un vero programmatore. Hai finito i nomi subito dopo John Doe e sei andato direttamente a "foo bar" :)
msouth,

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



4

Come altri hanno sottolineato, l' ALTER TABLEaffermazione di sqlite non supporta DROP COLUMNe la ricetta standard per farlo non conserva vincoli e indici.

Ecco un po 'di codice Python per farlo genericamente, mantenendo tutti i vincoli e gli indici chiave.

Si prega di eseguire il backup del database prima dell'uso! Questa funzione si basa sulla valutazione medica dell'istruzione CREATE TABLE originale ed è potenzialmente un po 'pericolosa, ad esempio farà una cosa sbagliata se un identificatore contiene una virgola o una parentesi incorporata.

Se qualcuno si preoccupasse di contribuire in modo migliore per analizzare l'SQL, sarebbe fantastico!

AGGIORNAMENTO Ho trovato un modo migliore per analizzare usando ilsqlparsepacchettoopen-source. Se c'è qualche interesse lo pubblicherò qui, lascia un commento per chiederlo ...

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

Mantiene anche le chiavi esterne al tavolo?
Lasse V. Karlsen,

@ LasseV.Karlsen Ho fatto alcuni test e dovrebbe mantenere i vincoli di chiave esterna poiché questi sembrano essere applicati dal nome della tabella.
spam_eggs

Come posso eseguirlo da Java?
Leos Literak,

4

Ho riscritto la risposta @Udinic in modo che il codice generi automaticamente la query di creazione della tabella . Inoltre non ha bisogno ConnectionSource. Deve anche farlo all'interno di una transazione .

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

DB Browser per SQLite consente di aggiungere o eliminare colonne.

Nella vista principale, scheda Database Structure, fai clic sul nome della tabella. Un pulsante Modify Tableviene abilitato, che apre una nuova finestra in cui è possibile selezionare la colonna / campo e rimuoverlo.


2

puoi usare Sqlitebrowser. Nella modalità browser, per il rispettivo database e la tabella, sotto la struttura tab -database, seguendo l'opzione Modifica tabella, è possibile rimuovere la rispettiva colonna.


2

Ho migliorato la risposta dell'utente2638929 e ora è possibile conservare il tipo di colonna, la chiave primaria, il valore predefinito ecc.

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. Ho usato qui android.arch.persistence.db.SupportSQLiteDatabase, ma puoi facilmente modificarlo per l'usoandroid.database.sqlite.SQLiteDatabase


2

Immagino che quello che vuoi fare sia la migrazione del database. 'Drop'ping una colonna non esiste in SQLite. Tuttavia, è possibile aggiungere una colonna aggiuntiva utilizzando la query della tabella ALTER.


1

È possibile utilizzare l'amministratore SQlite per modificare i nomi delle colonne. Fai clic con il tasto destro sul nome della tabella e seleziona Modifica tabella. Qui troverai la struttura della tabella e puoi rinominarla facilmente.


1

Poiché SQLite ha un supporto limitato ad ALTER TABLE, puoi solo AGGIUNGERE la colonna alla fine della tabella O MODIFICARE TABLE_NAME in SQLite.

Ecco la migliore risposta di COME ELIMINARE LA COLONNA DA SQLITE?

visita Elimina colonna dalla tabella SQLite


1

Come alternativa:

Se hai una tabella con schema

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

puoi usare CREATE TABLE...ASun'affermazione come CREATE TABLE person2 AS SELECT id, first_name, last_name, age FROM person;, ad esempio, tralasciare le colonne che non vuoi. Quindi rilasciare la persontabella originale e rinominare quella nuova.

Nota questo metodo produce una tabella senza PRIMARY KEY e nessun vincolo. Per preservarli, utilizzare i metodi descritti da altri per creare una nuova tabella o utilizzare una tabella temporanea come intermedio.


1

Questa risposta a una domanda diversa è orientata alla modifica di una colonna, ma credo che una parte della risposta potrebbe anche fornire un approccio utile se si dispone di molte colonne e non si desidera ridigitare la maggior parte a mano per la propria dichiarazione INSERT:

https://stackoverflow.com/a/10385666

È possibile eseguire il dump del database come descritto nel collegamento sopra, quindi acquisire l'istruzione "crea tabella" e un modello "Inserisci" da tale dump, quindi seguire le istruzioni nella voce Domande frequenti su SQLite "Come aggiungere o eliminare colonne da un esistente tabella in SQLite. " (Le FAQ sono collegate altrove in questa pagina.)


In realtà, mi sono appena reso conto che il dump non include i nomi delle colonne nell'inserto per impostazione predefinita. Quindi potrebbe essere altrettanto utile usare il pragma .schema per afferrare i nomi delle colonne, poiché dovrai quindi eliminare le dichiarazioni di tipo in entrambi i modi.
burpgrass,

1

Implementazione Pythonbasata su informazioni su 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)

Questo script crea innanzitutto una tabella temporanea casuale e inserisce i dati delle sole colonne necessarie tranne quella che verrà eliminata. Quindi ripristina la tabella originale in base alla tabella temporanea e rilascia la tabella temporanea.


1

La mia soluzione, devo solo chiamare questo metodo.

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

E metodo ausiliario per ottenere le colonne:

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

Questo metodo non mantiene il tipo colunm, quindi ho pubblicato una versione modificata del tuo codice
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+";");
            }
        }
    }
}

e basta chiamare un metodo

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

-1

Ora puoi anche usare il browser DB per SQLite per manipolare le colonne


-2

esempio per aggiungere una colonna: -

alter table student add column TOB time;

qui student è table_name e TOB è column_name da aggiungere.

Funziona e testato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.