Wie lösche oder füge ich eine Spalte in SQLITE hinzu?


239

Ich möchte eine Spalte in der SQLite-Datenbank löschen oder hinzufügen

Ich verwende die folgende Abfrage, um die Spalte zu löschen.

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

Aber es gibt Fehler

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

Antworten:


353

ALTER TABLE SQLite

SQLite unterstützt eine begrenzte Teilmenge von ALTER TABLE. Mit dem Befehl ALTER TABLE in SQLite kann der Benutzer eine Tabelle umbenennen oder einer vorhandenen Tabelle eine neue Spalte hinzufügen. Es ist nicht möglich, eine Spalte umzubenennen, eine Spalte zu entfernen oder Einschränkungen zu einer Tabelle hinzuzufügen oder daraus zu entfernen.

Sie können:

  1. Erstellen Sie eine neue Tabelle als die, die Sie ändern möchten.
  2. alle Daten kopieren,
  3. alten Tisch fallen lassen,
  4. benenne den neuen um.

47
stackoverflow.com/a/5987838/1578528 bietet ein grundlegendes Beispiel für die Ausführung der Aufgabe.
Bikram990

4
Vor dieser Sequenz und in Fällen, in denen externe Tabellen auf diese Tabelle verweisen, muss aufgerufen werden PRAGMA foreign_keys=OFF. In diesem Fall muss nach dieser Sequenz aufgerufen PRAGMA foreign_keys=ONwerden, um Fremdschlüssel wieder zu aktivieren.
PazO

Wie kopieren Sie auch den Freoign-Schlüssel und die Indizes?
Jonathan

Solange Sie zuerst eine neue Tabelle erstellen und nicht aus select erstellen, enthält sie all diese Dinge.
John Lord

1
In neueren SQLite-Versionen RENAME COLUMNwird unterstützt. 🎉 sqlite.org/releaselog/3_25_0.html
Grogs

46

Ich habe eine Java-Implementierung geschrieben, die auf der von Sqlite empfohlenen Methode basiert:

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

Um die Tabellenspalte zu erhalten, habe ich "PRAGMA table_info" verwendet:

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

Ich habe tatsächlich in meinem Blog darüber geschrieben. Weitere Erklärungen finden Sie dort:

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


1
Das ist ziemlich langsam, nicht wahr? für Big Data Tabellen?
Joran Beasley

2
Es wäre besser, wenn dies in einer einzigen Transaktion geschehen würde, als möglicherweise anderen Codes zu erlauben, Dinge in einem Übergangszustand zu sehen.
Donal Fellows

Dieser Code wird normalerweise beim Upgrade der Datenbank ausgeführt, wenn kein anderer Code gleichzeitig ausgeführt wird. Sie können eine Transaktion erstellen und alle darin enthaltenen Befehle ausführen.
Udinic

3
Ich bin mir ziemlich sicher, dass bei Verwendung dieser Lösung die Spalten in Ihrer Ergebnistabelle vollständig leer sind - es bleiben keine Typinformationen, PK, FK, Standardwerte, eindeutigen oder Prüfbeschränkungen übrig. Es wird lediglich der Spaltenname in die neue Tabelle importiert. Da Fremdschlüssel vor dem Ausführen nicht deaktiviert werden, können auch Daten in anderen Tabellen durcheinander geraten.
ACK_stoverflow

4
Alternativ können Sie anstelle einer INSERTAnweisung auch die neue Tabelle erstellen, indem Sie einen"CREAT TABLE" + tableName + "AS SELECT " + columnsSeperated + " FROM " + tableName + "_old;"
Robert

26

Wie andere darauf hingewiesen haben

Es ist nicht möglich, eine Spalte umzubenennen, eine Spalte zu entfernen oder Einschränkungen zu einer Tabelle hinzuzufügen oder daraus zu entfernen.

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

Sie können zwar immer eine neue Tabelle erstellen und dann die ältere löschen. Ich werde versuchen, diese Problemumgehung anhand eines Beispiels zu erläutern .

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       

Jetzt möchten Sie die Spalte heightaus dieser Tabelle entfernen .

Erstellen Sie eine weitere Tabelle mit dem Namen new_person

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

Kopieren Sie nun die Daten aus der alten Tabelle

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>

Löschen Sie nun die personTabelle und benennen Sie sie new_personin umperson

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

Wenn Sie also eine machen .schema, werden Sie sehen

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

Was ist mit ausländischen Referenzen? Oracle würde sich beschweren, wenn Sie eine Tabelle löschen, die von einer anderen Tabelle verwendet wird.
Leos Literak

6
Ich kann Ihnen sagen, dass Sie ein echter Programmierer sind. Du hast gleich nach John Doe
keine

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



4

Wie andere bereits betont haben, wird die ALTER TABLEAnweisung von sqlite nicht unterstützt DROP COLUMN, und das Standardrezept dazu bewahrt keine Einschränkungen und Indizes.

Hier ist ein Python-Code, um dies generisch zu tun, während alle wichtigen Einschränkungen und Indizes beibehalten werden.

Bitte sichern Sie Ihre Datenbank vor der Verwendung! Diese Funktion basiert auf der Bearbeitung der ursprünglichen CREATE TABLE-Anweisung und ist möglicherweise etwas unsicher. Beispielsweise wird sie falsch ausgeführt, wenn ein Bezeichner ein eingebettetes Komma oder eine Klammer enthält.

Wenn jemand einen besseren Weg zum Parsen von SQL einbringen möchte, wäre das großartig!

UPDATE Ich habe einen besseren Weg gefunden, um mit dem Open-Source-sqlparsePaketzu analysieren. Wenn es Interesse gibt, werde ich es hier posten, hinterlasse einfach einen Kommentar und frage danach ...

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

Behält es auch Fremdschlüssel für die Tabelle bei?
Lasse V. Karlsen

@ LasseV.Karlsen Ich habe einige Tests durchgeführt und es sollten Fremdschlüsseleinschränkungen beibehalten werden, da diese durch den Tabellennamen erzwungen zu werden scheinen.
spam_eggs

Wie kann ich es von Java aus ausführen?
Leos Literak

4

Ich habe die @ Udinic-Antwort so umgeschrieben , dass der Code automatisch eine Abfrage zur Tabellenerstellung generiert . Es braucht auch nicht ConnectionSource. Dies muss auch innerhalb einer Transaktion erfolgen .

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

Mit DB Browser for SQLite können Sie Spalten hinzufügen oder löschen.

Database StructureKlicken Sie in der Hauptansicht auf der Registerkarte auf den Tabellennamen. Eine Schaltfläche Modify Tablewird aktiviert, die ein neues Fenster öffnet, in dem Sie die Spalte / das Feld auswählen und entfernen können.


2

Sie können Sqlitebrowser verwenden. Im Browsermodus konnte für die jeweilige Datenbank und die Tabelle unter der Registerkarte -Datenbankstruktur nach der Option Tabelle ändern die entsprechende Spalte entfernt werden.


2

Ich denke, was Sie tun möchten, ist die Datenbankmigration. Das Löschen einer Spalte ist in SQLite nicht vorhanden. Sie können jedoch mithilfe der ALTER-Tabellenabfrage eine zusätzliche Spalte hinzufügen.


1

Sie können den SQlite Administrator zum Ändern der Spaltennamen verwenden. Klicken Sie mit der rechten Maustaste auf Tabellenname und wählen Sie Tabelle bearbeiten. Hier finden Sie die Tabellenstruktur und können sie einfach umbenennen.


1

Da SQLite ALTER TABLE nur eingeschränkt unterstützt, können Sie in SQLite nur die Spalte ADD am Ende der Tabelle hinzufügen ODER TABLE_NAME ÄNDERN.

Hier ist die beste Antwort auf WIE MAN SPALTE VON SQLITE LÖSCHT.

Besuchen Sie Spalte aus SQLite-Tabelle löschen


1

Als Alternative:

Wenn Sie eine Tabelle mit Schema haben

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

Sie können eine CREATE TABLE...ASAnweisung wie verwenden CREATE TABLE person2 AS SELECT id, first_name, last_name, age FROM person;, dh lassen Sie die Spalten weg, die Sie nicht möchten. Löschen Sie dann die ursprüngliche personTabelle und benennen Sie die neue um.

Beachten Sie, dass diese Methode eine Tabelle ohne PRIMARY KEY und ohne Einschränkungen erstellt. Um diese beizubehalten, verwenden Sie die anderen beschriebenen Methoden, um eine neue Tabelle zu erstellen, oder verwenden Sie eine temporäre Tabelle als Zwischenstufe.


1

Diese Antwort auf eine andere Frage zielt darauf ab, eine Spalte zu ändern, aber ich glaube, ein Teil der Antwort könnte auch einen nützlichen Ansatz liefern, wenn Sie viele Spalten haben und die meisten nicht manuell für Ihre INSERT-Anweisung erneut eingeben möchten:

https://stackoverflow.com/a/10385666

Sie können Ihre Datenbank wie im obigen Link beschrieben sichern, dann die Anweisung "create table" und eine Vorlage zum Einfügen aus diesem Speicherauszug abrufen und dann den Anweisungen im SQLite-FAQ-Eintrag "Wie füge ich Spalten zu einer vorhandenen hinzu oder lösche sie?" Befolgen Tabelle in SQLite. " (FAQ ist an anderer Stelle auf dieser Seite verlinkt.)


Eigentlich habe ich gerade festgestellt, dass der Speicherauszug standardmäßig keine Spaltennamen in der Einfügung enthält. Daher ist es möglicherweise genauso gut, das Pragma .schema zum Abrufen von Spaltennamen zu verwenden, da Sie dann die Typdeklarationen in beiden Fällen löschen müssen.
Burpgrass

1

Implementierung Pythonbasierend auf Informationen unter 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)

Dieses Skript erstellt zunächst eine zufällige temporäre Tabelle und fügt nur Daten der erforderlichen Spalten ein, mit Ausnahme derjenigen, die gelöscht werden. Stellt dann die ursprüngliche Tabelle basierend auf der temporären Tabelle wieder her und löscht die temporäre Tabelle.


1

Meine Lösung muss nur diese Methode aufrufen.

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

Und Hilfsmethode, um die Spalten zu erhalten:

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

Diese Methode behält nicht colunm Typ, also habe ich modifizierte Version Ihres Codes veröffentlicht
Berdimurat Masaliev

1

Ich habe die Antwort von user2638929 verbessert und jetzt können Spaltentyp , Primärschlüssel, Standardwert usw. beibehalten werden.

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. Ich habe es hier verwendet android.arch.persistence.db.SupportSQLiteDatabase, aber Sie können es einfach für die Verwendung ändernandroid.database.sqlite.SQLiteDatabase


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

und rufen Sie einfach eine Methode auf

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

-1

Beispiel zum Hinzufügen einer Spalte: -

alter table student add column TOB time;

hier Schüler ist table_name und TOB ist Spalten_Name hinzugefügt werden.

Es funktioniert und getestet.


-1

Sie können jetzt auch den DB-Browser für SQLite verwenden, um Spalten zu bearbeiten

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.