Видалити стовпчик із таблиці SQLite


114

У мене проблема: мені потрібно видалити стовпчик зі своєї бази даних SQLite. Я написав цей запит

alter table table_name drop column column_name 

але це не працює. Будь ласка, допоможи мені.

Відповіді:


207

Від: http://www.sqlite.org/faq.html :

(11) Як додати або видалити стовпці з існуючої таблиці в SQLite.

SQLite має обмежену підтримку ALTER TABLE, яку можна використовувати для додавання стовпця в кінець таблиці або для зміни назви таблиці. Якщо ви хочете внести більш складні зміни в структуру таблиці, вам доведеться відтворити таблицю. Ви можете зберегти наявні дані до тимчасової таблиці, скинути стару таблицю, створити нову таблицю, а потім скопіювати дані назад з тимчасової таблиці.

Наприклад, припустимо, що у вас є таблиця з назвою "t1" із назвами стовпців "a", "b" і "c", і ви хочете видалити стовпець "c" з цієї таблиці. Наступні кроки ілюструють, як це можна зробити:

BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;

8
+ Завжди читайте документацію на SQLite. Коли ви отримаєте помилки, ви помітите занадто багато обмежень та відмінностей у граматиці SQL. Документацію на SQLite зрозуміти дуже просто. Не хвилюйся з цього приводу.
AhmetB - Google

2
Вам потрібно виконати команду VACUUM після видалення стовпців для безпеки; без вакуумування файл бази даних все ще містить дані видалених стовпців.
jj1bdx

@ jj1bdx Я не думаю, що він все ще містить дані, але "невикористаний простір на диску додається до внутрішнього" вільного списку "і повторно використовується наступного разу, коли ви вставляєте дані. Дисковий простір не втрачається. Але також це не є повернуто в операційну систему. " як цитується з веб-сайту sqlite3.
Гільгерме Саломе

Як я використовувати кілька вилучень стовпців в одній транзакції це працювало тільки тоді , коли я видалив TEMPORARYз CREATE TABLE.
ефемер

Є моя реалізація за допомогою QSqlQuery Qt: gist.github.com/ephemerr/568d0d41bc389ec78f9fb7d1f015a82a
ephemerr

56

Замість того, щоб викидати резервну таблицю, просто перейменуйте її ...

BEGIN TRANSACTION;
CREATE TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
ALTER TABLE t1_backup RENAME TO t1;
COMMIT;

6
Він не працюватиме, коли до вас підключений ключ передбачення t1.
ефемер

39

Для простоти, чому б не створити таблицю резервного копіювання з оператора select?

CREATE TABLE t1_backup AS SELECT a, b FROM t1;
DROP TABLE t1;
ALTER TABLE t1_backup RENAME TO t1;

3
Цей підхід, схоже, зберігає типи даних стовпців, тоді як, схоже, прийнята відповідь призводить до того, що всі стовпці мають тип TEXT.
Уве Кейм

2
Ця заява також повинна бути зафіксована в транзакції.
Георг Шолі

10
Зауважте, що це не зберігає первинний ключ, а sqlite не підтримує зміни таблиць для додавання первинного ключа. Отже, якщо важливий первинний ключ, це не те, що вам слід використовувати
Тім

2
Це також не зберігає NOT NULL.
FutureShocked

прийнята відповідь прекрасно працює. Ви повинні вказати типи даних під час створення таблиці. Зітхнути.
Джон Лорд

8

Цей параметр працює лише в тому випадку, якщо ви можете відкрити БД у веб-переглядачі БД, як-то браузер DB для SQLite .

У браузері DB для SQLite:

  1. Перейдіть на вкладку "Структура бази даних"
  2. Вибір таблиці Виберіть таблицю Змінити (просто під вкладками)
  3. Виберіть стовпець, який потрібно видалити
  4. Клацніть на поле Видалити та натисніть кнопку ОК

3

=> Створіть нову таблицю безпосередньо за допомогою наступного запиту:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> Тепер вставте дані в ім'я таблиці з існуючої таблиці за допомогою наступного запиту:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> Тепер відкиньте існуючу таблицю, виконавши наступний запит:

DROP TABLE existing_table;

1

Для SQLite3 c ++:

void GetTableColNames( tstring sTableName , std::vector<tstring> *pvsCols )
{
    UASSERT(pvsCols);

    CppSQLite3Table table1;

    tstring sDML = StringOps::std_sprintf(_T("SELECT * FROM %s") , sTableName.c_str() );



    table1 = getTable( StringOps::tstringToUTF8string(sDML).c_str() );

    for ( int nCol = 0 ; nCol < table1.numFields() ; nCol++ )
    {
        const char* pch1 = table1.fieldName(nCol);  

        pvsCols->push_back( StringOps::UTF8charTo_tstring(pch1));
    }
}


bool ColExists( tstring sColName )
{
    bool bColExists = true;

    try
    {
        tstring sQuery = StringOps::std_sprintf(_T("SELECT %s FROM MyOriginalTable LIMIT 1;") , sColName.c_str() );

        ShowVerbalMessages(false);

        CppSQLite3Query q = execQuery( StringOps::tstringTo_stdString(sQuery).c_str() );

        ShowVerbalMessages(true);
    }
    catch (CppSQLite3Exception& e)
    {
        bColExists = false;
    }

    return bColExists;
}

void DeleteColumns( std::vector<tstring> *pvsColsToDelete )
{
    UASSERT(pvsColsToDelete);

    execDML( StringOps::tstringTo_stdString(_T("begin transaction;")).c_str() );


    std::vector<tstring> vsCols;
    GetTableColNames( _T("MyOriginalTable") , &vsCols );


    CreateFields( _T("TempTable1") , false );

    tstring sFieldNamesSeperatedByCommas;

    for ( int nCol = 0 ; nCol < vsCols.size() ; nCol++ )
    {

        tstring sColNameCurr = vsCols.at(nCol);

        bool bUseCol = true;

        for ( int nColsToDelete = 0; nColsToDelete < pvsColsToDelete->size() ; nColsToDelete++ )
        {
            if ( pvsColsToDelete->at(nColsToDelete) == sColNameCurr )
            {
                bUseCol = false;
                break;
            }
        }

        if ( bUseCol )
            sFieldNamesSeperatedByCommas+= (sColNameCurr + _T(","));

    }

    if ( sFieldNamesSeperatedByCommas.at( int(sFieldNamesSeperatedByCommas.size()) - 1) == _T(','))
        sFieldNamesSeperatedByCommas.erase( int(sFieldNamesSeperatedByCommas.size()) - 1 );

    tstring sDML;


    sDML = StringOps::std_sprintf(_T("insert into TempTable1 SELECT %s FROM MyOriginalTable;\n") , sFieldNamesSeperatedByCommas.c_str() );
    execDML( StringOps::tstringTo_stdString(sDML).c_str() );

    sDML = StringOps::std_sprintf(_T("ALTER TABLE MyOriginalTable RENAME TO MyOriginalTable_old\n") );
    execDML( StringOps::tstringTo_stdString(sDML).c_str() );

    sDML = StringOps::std_sprintf(_T("ALTER TABLE TempTable1 RENAME TO MyOriginalTable\n") );
    execDML( StringOps::tstringTo_stdString(sDML).c_str() );


    sDML = ( _T("DROP TABLE MyOriginalTable_old;") );   
    execDML( StringOps::tstringTo_stdString(sDML).c_str() );


    execDML( StringOps::tstringTo_stdString(_T("commit transaction;")).c_str() );   
}

1

Я створив функцію Python, де ви вводите таблицю та стовпець, щоб видалити як аргументи:

def removeColumn(table, column):
    columns = []
    for row in c.execute('PRAGMA table_info(' + table + ')'):
        columns.append(row[1])
    columns.remove(column)
    columns = str(columns)
    columns = columns.replace("[", "(")
    columns = columns.replace("]", ")")
    for i in ["\'", "(", ")"]:
        columns = columns.replace(i, "")
    c.execute('CREATE TABLE temptable AS SELECT ' + columns + ' FROM ' + table)
    c.execute('DROP TABLE ' + table)
    c.execute('ALTER TABLE temptable RENAME TO ' + table)
    conn.commit()

Відповідно до інформації про відповіді Дуди та MeBigFatGuy, це не спрацює, якщо на столі є зовнішній ключ, але це можна виправити за допомогою двох рядків коду (створення нової таблиці, а не просто перейменування тимчасової таблиці)


Що таке c? Що таке conn? Ця відповідь робить занадто багато припущень про доступні змінні невідомого типу.
Іван Кастелланос

0

Якщо комусь потрібна (майже) готова до використання функція PHP, на цій відповіді ґрунтується наступне :

/**
 * Remove a column from a table.
 * 
 * @param string $tableName The table to remove the column from.
 * @param string $columnName The column to remove from the table.
 */
public function DropTableColumn($tableName, $columnName)
{
    // --
    // Determine all columns except the one to remove.

    $columnNames = array();

    $statement = $pdo->prepare("PRAGMA table_info($tableName);");
    $statement->execute(array());
    $rows = $statement->fetchAll(PDO::FETCH_OBJ);

    $hasColumn = false;

    foreach ($rows as $row)
    {
        if(strtolower($row->name) !== strtolower($columnName))
        {
            array_push($columnNames, $row->name);
        }
        else
        {
            $hasColumn = true;
        }
    }

    // Column does not exist in table, no need to do anything.
    if ( !$hasColumn ) return;

    // --
    // Actually execute the SQL.

    $columns = implode('`,`', $columnNames);

    $statement = $pdo->exec(
       "CREATE TABLE `t1_backup` AS SELECT `$columns` FROM `$tableName`;
        DROP TABLE `$tableName`;
        ALTER TABLE `t1_backup` RENAME TO `$tableName`;");
}

На відміну від інших відповідей, SQL, використовуваний у цьому підході, схоже, зберігає типи даних стовпців, тоді як щось на зразок прийнятої відповіді, як видається, призводить до того, що всі стовпці мають тип TEXT.

Оновлення 1:

Використовуваний SQL має недолік, що autoincrementстовпці не зберігаються.


0

Про всяк випадок, якщо це могло б допомогти комусь, як я.

На основі офіційного веб-сайту та прийнятої відповіді я зробив код за допомогою C #, який використовує пакет System.Data.SQLite NuGet.

Цей код також зберігає первинний і зовнішній ключ .

КОД у C #:

void RemoveColumnFromSqlite (string tableName, string columnToRemove) {
 try {
    var mSqliteDbConnection = new SQLiteConnection ("Data Source=db_folder\\MySqliteBasedApp.db;Version=3;Page Size=1024;");
    mSqliteDbConnection.Open ();             
    // Reads all columns definitions from table
    List<string> columnDefinition = new List<string> ();
    var mSql = $"SELECT type, sql FROM sqlite_master WHERE tbl_name='{tableName}'";
    var mSqliteCommand = new SQLiteCommand (mSql, mSqliteDbConnection);
    string sqlScript = "";
    using (mSqliteReader = mSqliteCommand.ExecuteReader ()) {
       while (mSqliteReader.Read ()) {
          sqlScript = mSqliteReader["sql"].ToString ();
          break;
       }
    }
    if (!string.IsNullOrEmpty (sqlScript)) {
       // Gets string within first '(' and last ')' characters
       int firstIndex = sqlScript.IndexOf ("(");
       int lastIndex = sqlScript.LastIndexOf (")");
       if (firstIndex >= 0 && lastIndex <= sqlScript.Length - 1) {
          sqlScript = sqlScript.Substring (firstIndex, lastIndex - firstIndex + 1);
       }
       string[] scriptParts = sqlScript.Split (new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
       foreach (string s in scriptParts) {
          if (!s.Contains (columnToRemove)) {
             columnDefinition.Add (s);
          }
       }
    }
    string columnDefinitionString = string.Join (",", columnDefinition);
    // Reads all columns from table
    List<string> columns = new List<string> ();
    mSql = $"PRAGMA table_info({tableName})";
    mSqliteCommand = new SQLiteCommand (mSql, mSqliteDbConnection);
    using (mSqliteReader = mSqliteCommand.ExecuteReader ()) {
       while (mSqliteReader.Read ()) columns.Add (mSqliteReader["name"].ToString ());
    }
    columns.Remove (columnToRemove);
    string columnString = string.Join (",", columns);
    mSql = "PRAGMA foreign_keys=OFF";
    mSqliteCommand = new SQLiteCommand (mSql, mSqliteDbConnection);
    int n = mSqliteCommand.ExecuteNonQuery ();
    // Removes a column from the table
    using (SQLiteTransaction tr = mSqliteDbConnection.BeginTransaction ()) {
       using (SQLiteCommand cmd = mSqliteDbConnection.CreateCommand ()) {
          cmd.Transaction = tr;
          string query = $"CREATE TEMPORARY TABLE {tableName}_backup {columnDefinitionString}";
          cmd.CommandText = query;
          cmd.ExecuteNonQuery ();
          cmd.CommandText = $"INSERT INTO {tableName}_backup SELECT {columnString} FROM {tableName}";
          cmd.ExecuteNonQuery ();
          cmd.CommandText = $"DROP TABLE {tableName}";
          cmd.ExecuteNonQuery ();
          cmd.CommandText = $"CREATE TABLE {tableName} {columnDefinitionString}";
          cmd.ExecuteNonQuery ();
          cmd.CommandText = $"INSERT INTO {tableName} SELECT {columnString} FROM {tableName}_backup;";
          cmd.ExecuteNonQuery ();
          cmd.CommandText = $"DROP TABLE {tableName}_backup";
          cmd.ExecuteNonQuery ();
       }
       tr.Commit ();
    }
    mSql = "PRAGMA foreign_keys=ON";
    mSqliteCommand = new SQLiteCommand (mSql, mSqliteDbConnection);
    n = mSqliteCommand.ExecuteNonQuery ();
 } catch (Exception ex) {
    HandleExceptions (ex);
 }
}

0
PRAGMA foreign_keys=off;

BEGIN TRANSACTION;

ALTER TABLE table1 RENAME TO _table1_old;

CREATE TABLE table1 (
( column1 datatype [ NULL | NOT NULL ],
  column2 datatype [ NULL | NOT NULL ],
  ...
);

INSERT INTO table1 (column1, column2, ... column_n)
  SELECT column1, column2, ... column_n
  FROM _table1_old;

COMMIT;

PRAGMA foreign_keys=on;

Для отримання додаткової інформації: https://www.techonthenet.com/sqlite/tables/alter_table.php

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.