Eliminar columna de la tabla SQLite

114

Tengo un problema: necesito eliminar una columna de mi base de datos SQLite. Escribí esta consulta

alter table table_name drop column column_name 

Pero no funciona. Por favor, ayúdame.

arenoso
fuente

Respuestas:

207

De: http://www.sqlite.org/faq.html :

(11) ¿Cómo agrego o elimino columnas de una tabla existente en SQLite?

SQLite tiene soporte ALTER TABLE limitado que puede usar para agregar una columna al final de una tabla o para cambiar el nombre de una tabla. Si desea realizar cambios más complejos en la estructura de una tabla, tendrá que volver a crear la tabla. Puede guardar los datos existentes en una tabla temporal, eliminar la tabla anterior, crear la nueva tabla y luego copiar los datos de la tabla temporal.

Por ejemplo, suponga que tiene una tabla llamada "t1" con los nombres de columnas "a", "b" y "c" y que desea eliminar la columna "c" de esta tabla. Los siguientes pasos ilustran cómo se puede hacer esto:

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;
MeBigFatGuy
fuente
8
+ Siempre lea la documentación de SQLite. Notará demasiadas limitaciones y diferencias en la gramática SQL cuando obtenga errores. La documentación de SQLite es muy fácil de entender. No se preocupe por eso.
AhmetB - Google
2
Debe ejecutar el comando VACUUM después de eliminar las columnas por motivos de seguridad; sin aspirar, el archivo de la base de datos todavía contiene los datos de las columnas eliminadas.
jj1bdx
@ jj1bdx No creo que todavía contenga los datos, pero "el espacio en disco no utilizado se agrega a una" lista libre "interna y se reutiliza la próxima vez que inserta datos. El espacio en disco no se pierde. Pero tampoco devuelto al sistema operativo ". como se cita en el sitio web sqlite3.
Guilherme Salomé
Como ya he utilizado múltiples extracciones de columna en una transacción esta funcionaba sólo cuando quité TEMPORARYde CREATE TABLE.
efemérides
Existe mi implementación usando QSqlQuery de Qt: gist.github.com/ephemerr/568d0d41bc389ec78f9fb7d1f015a82a
ephemerr
56

En lugar de eliminar la tabla de respaldo, simplemente cámbiele el nombre ...

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;
Duda
fuente
6
No funcionará cuando tenga una clave extranjera conectada t1.
efemérides
39

Para simplificar, ¿por qué no crear la tabla de respaldo a partir de la instrucción select?

CREATE TABLE t1_backup AS SELECT a, b FROM t1;
DROP TABLE t1;
ALTER TABLE t1_backup RENAME TO t1;
user4086833
fuente
3
Este enfoque parece preservar los tipos de datos de las columnas, mientras que algo como la respuesta aceptada parece dar como resultado que todas las columnas sean de tipo TEXT.
Uwe Keim
2
Esta declaración también debe estar envuelta en una transacción.
Georg Schölly
10
Tenga en cuenta que esto no conserva la clave principal y sqlite no admite la modificación de tablas para agregar la clave principal. Entonces, si la clave principal es importante, esto no es lo que debe usar
Tim
2
Esto tampoco conserva NOT NULL.
FutureShocked
la respuesta aceptada funciona bien. Se supone que debe especificar los tipos de datos cuando crea la tabla. Suspiro.
John Lord
8

Esta opción solo funciona si puede abrir la base de datos en un navegador de bases de datos como DB Browser para SQLite .

En DB Browser para SQLite:

  1. Vaya a la pestaña "Estructura de la base de datos"
  2. Seleccione su tabla Seleccione Modificar tabla (justo debajo de las pestañas)
  3. Seleccione la columna que desea eliminar
  4. Haga clic en Eliminar campo y haga clic en Aceptar
MagTun
fuente
3

=> Cree una nueva tabla directamente con la siguiente consulta:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> Ahora inserte los datos en table_name de existing_table con la siguiente consulta:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> Ahora suelte la tabla existente mediante la siguiente consulta:

DROP TABLE existing_table;
usuario3317939
fuente
1

Para 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() );   
}
Soleado127
fuente
1

Hice una función de Python en la que ingresas la tabla y la columna para eliminar como argumentos:

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

Según la información de las respuestas de Duda y MeBigFatGuy, esto no funcionará si hay una clave externa en la tabla, pero esto se puede solucionar con 2 líneas de código (creando una nueva tabla y no solo cambiando el nombre de la tabla temporal)

Dom
fuente
¿Qué es c? ¿Qué es conn? Esta respuesta hace demasiadas suposiciones sobre las variables disponibles de tipo desconocido.
Ivan Castellanos hace
0

En caso de que alguien necesite una función PHP (casi) lista para usar, lo siguiente se basa en esta respuesta :

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

En contraste con otras respuestas, el SQL usado en este enfoque parece preservar los tipos de datos de las columnas, mientras que algo como la respuesta aceptada parece resultar en que todas las columnas sean de tipo TEXT.

Actualización 1:

El SQL utilizado tiene el inconveniente de que las autoincrementcolumnas no se conservan.

Uwe Keim
fuente
0

Por si acaso pudiera ayudar a alguien como yo.

Basado en el sitio web oficial y la respuesta aceptada , hice un código usando C # que usa System.Data.SQLite paquete NuGet.

Este código también conserva la clave principal y la clave externa .

CÓDIGO en 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);
 }
}
Naveen Kumar V
fuente
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;

Para más información: https://www.techonthenet.com/sqlite/tables/alter_table.php

Nexus242
fuente