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
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:
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/
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
);
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
Nous ne pouvons pas supprimer une colonne spécifique dans SQLite 3. Voir le FAQ .
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();
}
}
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
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.
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.
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?
Navigateur de base de données pour SQLite vous permet d’ajouter ou de supprimer des colonnes.
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.
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.)
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.
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");
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
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;
}
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.
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é.