Как удалить или добавить столбец в SQLITE? - PullRequest
210 голосов
/ 09 декабря 2011

Я хочу удалить или добавить столбец в базе данных sqlite

Я использую следующий запрос для удаления столбца.

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

Но выдает ошибку

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

Ответы [ 20 ]

319 голосов
/ 09 декабря 2011

ALTER TABLE SQLite

SQLite поддерживает ограниченное подмножество ALTER TABLE.Команда ALTER TABLE в SQLite позволяет пользователю переименовать таблицу или добавить новый столбец в существующую таблицу.Невозможно переименовать столбец, удалить столбец или добавить или удалить ограничения из таблицы.

Вы можете:

  1. создать новую таблицу как ту, которую выпытаемся изменить,
  2. копировать все данные,
  3. удалить старую таблицу,
  4. переименовать новую.
44 голосов
/ 14 мая 2012

Я написал реализацию Java на основе рекомендованного 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;");
}

Чтобы получить столбец таблицы, я использовал «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;
}

На самом деле я написал об этом в своем блоге, там вы можете найти больше объяснений:

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

17 голосов
/ 17 сентября 2015

Как уже отмечали другие

Невозможно переименовать столбец, удалить столбец или добавить или удалить ограничения из таблицы.

источник: http://www.sqlite.org/lang_altertable.html

Хотя вы всегда можете создать новую таблицу, а затем отбросить более старую. Я попытаюсь объяснить этот обходной путь на примере.

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       

Теперь вы хотите удалить столбец height из этой таблицы.

Создайте еще одну таблицу с именем new_person

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

Теперь скопируйте данные из старой таблицы

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>

Теперь удалите таблицу person и переименуйте new_person в person

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

Так что теперь, если вы сделаете .schema, вы увидите

sqlite>.schema
CREATE TABLE "person"(
 id INTEGER PRIMARY KEY, 
 first_name TEXT, 
 last_name TEXT, 
 age INTEGER 
);
12 голосов
/ 09 декабря 2011

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

Как видно на диаграмме, поддерживается только ADD COLUMN.Существует (довольно тяжелый) обходной путь, хотя: http://www.sqlite.org/faq.html#q11

4 голосов
/ 13 декабря 2016

Я переписал @ Udinic ответ , чтобы код автоматически генерировал запрос на создание таблицы .Также не нужно 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();
    }


}
.
4 голосов
/ 26 сентября 2012

Мы не можем удалить определенный столбец в SQLite 3. См. FAQ .

3 голосов
/ 02 сентября 2014

Как уже отмечали другие, оператор ALTER TABLE sqlite поддерживает , а не поддерживает DROP COLUMN, и стандартный рецепт для этого не сохраняет ограничений и индексов.

Вот некоторый Pythonкод для этого, в то время как поддерживает все ключевые ограничения и индексы.

Пожалуйста, сделайте резервную копию вашей базы данных перед использованием! Эта функция полагается на лечение оригинального оператора CREATE TABLE и потенциально немного небезопасна - например, она будет работать неправильно, если идентификатор содержитвстроенная запятая или скобка.

Если кто-то захочет внести свой вклад в лучший способ разбора SQL, это было бы здорово!

ОБНОВЛЕНИЕ Я нашел лучший способ анализа с использованием пакета с открытым исходным кодом sqlparse.Если есть интерес, я опубликую его здесь, просто оставьте комментарий с просьбой об этом ...

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
1 голос
/ 05 февраля 2018

Реализация в Python на основе информации в 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)

Этот скрипт сначала создает случайную временную таблицу и вставляет данные только необходимых столбцов, кроме того, который будет отброшен. Затем восстанавливает исходную таблицу на основе временной таблицы и удаляет временную таблицу.

1 голос
/ 15 июня 2019

Полагаю, вы хотите перенести базу данных. «Отбрасывание» столбца не существует в SQLite. Однако вы можете добавить дополнительный столбец, используя запрос таблицы ALTER.

1 голос
/ 17 февраля 2014

Поскольку SQLite имеет ограниченную поддержку ALTER TABLE, поэтому вы можете только добавить столбец ADD в конец таблицы ИЛИ ИЗМЕНИТЬ TABLE_NAME в SQLite.

Вот лучший ответ, КАК УДАЛИТЬ КОЛОННУ ИЗ SQLITE?

визит Удалить столбец из таблицы SQLite

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...