web-dev-qa-db-fra.com

Comment supprimer ou ajouter une colonne en SQLITE?

Je veux supprimer ou ajouter une colonne dans la base de données sqlite

J'utilise la requête suivante pour supprimer la colonne.

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

Mais ça donne une erreur 

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

ALTER TABLE SQLite

SQLite prend en charge un sous-ensemble limité d'ALTER TABLE. La commande ALTER TABLE dans SQLite permet à l'utilisateur de renommer une table ou d'ajouter une nouvelle colonne à une table existante. Il n'est pas possible de renommer une colonne, de supprimer une colonne ou d'ajouter ou de supprimer des contraintes d'une table.

Vous pouvez:

  1. créer une nouvelle table comme celle que vous essayez de changer,
  2. copier toutes les données,
  3. déposer la vieille table,
  4. renommer le nouveau.
307
Michał Powaga

J'ai écrit une implémentation Java basée sur la méthode recommandée par le Sqlite:

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

Pour obtenir la colonne de la table, j'ai utilisé le "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;
}

En fait, j’en ai écrit sur mon blog, vous pouvez y voir plus d’explications:

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

42
Udinic

Comme d'autres l'ont souligné 

Il n'est pas possible de renommer une colonne, de supprimer une colonne ou d'ajouter ou supprimer les contraintes d'une table.

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

Bien que vous puissiez toujours créer une nouvelle table, puis supprimez la plus ancienne. Je vais essayer d’expliquer cette solution de contournement avec un exemple.

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       

Vous voulez maintenant supprimer la colonne height de cette table.

Créez une autre table appelée new_person 

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

Maintenant, copiez les données de l'ancienne table 

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>

Maintenant, supprimez la table person et renommez new_person en person

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

Alors maintenant, si vous faites un .schema, vous verrez

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

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

Comme vous pouvez le voir dans le diagramme, seul ADD COLUMN est pris en charge. Il existe une solution de contournement (un peu lourde), cependant: http://www.sqlite.org/faq.html#q11

12
LeleDumbo

Nous ne pouvons pas supprimer une colonne spécifique dans SQLite 3. Voir le FAQ .

4

J'ai récrit le @Udinic answer afin que le code génère la requête de création de table automatiquement. De plus, il n’a pas besoin de ConnectionSource. Il doit également le faire dans une transaction .

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


}
4
soshial

Comme d'autres l'ont déjà souligné, l'instruction ALTER TABLE de sqlite ne prend pas en charge DROP COLUMN, et la recette standard permettant de le faire ne conserve pas les contraintes et les index.

Voici un code python pour le faire de manière générique, tout en maintenant toutes les contraintes et index clés. 

S'il vous plaît sauvegarder votre base de données avant d'utiliser! Cette fonction repose sur la modification de l'instruction CREATE TABLE d'origine et présente un risque potentiel pour la sécurité. Par exemple, elle agira de manière incorrecte si un identificateur contient une virgule ou une parenthèse incorporée.

Si quelqu'un souhaitait contribuer à une meilleure façon d'analyser le code SQL, ce serait formidable!

UPDATEJ'ai trouvé un meilleur moyen d'analyser à l'aide du paquetage open-source sqlparse. S'il y a un intérêt, je le posterai ici, laissez un commentaire pour le demander ...

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
3
spam_eggs

Implémentation dans Python sur la base des informations de 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)

Ce script crée d’abord une table temporaire aléatoire et n’insère que les données des colonnes nécessaires, à l’exception de celle qui sera supprimée. Restaure ensuite la table d'origine en fonction de la table temporaire et supprime la table temporaire.

1
Akif

Je suppose que ce que vous voulez faire est la migration de la base de données. 'Supprimer une colonne n'existe pas dans SQLite. Mais vous pouvez cependant ajouter une colonne supplémentaire en utilisant la requête de table ALTER.

1
Subha_26

Comme SQLite a un support limité pour ALTER TABLE, vous ne pouvez donc ajouter que des colonnes à la fin de la table OR.

Voici la meilleure réponse de COMMENT SUPPRIMER COLUMN DE SQLITE?

visiter Supprimer la colonne de la table SQLite

1
Gaurav Singla

Navigateur de base de données pour SQLite vous permet d’ajouter ou de supprimer des colonnes.

1
rams

vous pouvez utiliser Sqlitebrowser. En mode navigateur, pour la base de données respective et la table, sous la structure à onglet-base de données, après l'option Modifier la table, la colonne correspondante peut être supprimée.

0
iamigham

Cette réponse à une question différente est orientée vers modifier une colonne, mais je pense qu'une partie de la réponse pourrait également fournir une approche utile si vous avez beaucoup de colonnes et que vous ne voulez pas les retaper à la main pour votre instruction INSERT: 

https://stackoverflow.com/a/10385666

Vous pouvez vider votre base de données comme décrit dans le lien ci-dessus, puis récupérer l'instruction "create table" et un modèle "insert" de ce dump, puis suivre les instructions de l'entrée SQLite FAQ "Comment ajouter ou supprimer des éléments colonnes d'une table existante dans SQLite. " (La FAQ est liée ailleurs sur cette page.)

0
burpgrass

Vous pouvez utiliser l'administrateur SQlite pour modifier les noms de colonne . Cliquez avec le bouton droit de la souris sur le nom de la table et sélectionnez Modifier la table.Vous y trouverez la structure de la table et vous pourrez facilement la renommer.

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

et juste appeler une méthode

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

J'ai amélioré utilisateur2638929 répondre et maintenant il peut préserver le type de colonne, la clé primaire, la valeur par défaut, etc. 

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. J'ai utilisé ici Android.Arch.persistence.db.SupportSQLiteDatabase, mais vous pouvez facilement le modifier pour l'utiliser Android.database.sqlite.SQLiteDatabase

0
Berdimurat Masaliev

Ma solution, seulement besoin d'appeler cette méthode.

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

Et méthode auxiliaire pour obtenir les colonnes:

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

Comme alternative:

Si vous avez une table avec schéma

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

vous pouvez utiliser une instruction CREATE TABLE...AS telle que CREATE TABLE person2 AS SELECT id, first_name, last_name, age FROM person;, c’est-à-dire omettre les colonnes que vous ne voulez pas. Ensuite, supprimez la table person d'origine et renommez la nouvelle.

Notez que cette méthode produit une table qui n'a pas de clé primaire ni de contrainte. Pour les conserver, utilisez les méthodes décrites par d’autres pour créer une nouvelle table ou utilisez une table temporaire comme intermédiaire.

0
karkarlawawa

exemple pour ajouter une colonne: -

alter table student add column TOB time;

ici student is nom_table etTOBest nom_colonne à ajouter.

Cela fonctionne et a testé.

0
Daredevil Kallol