Supprimer la colonne de la table SQLite


114

J'ai un problème: je dois supprimer une colonne de ma base de données SQLite. J'ai écrit cette requête

alter table table_name drop column column_name 

mais ça ne marche pas. Aidez-moi, s'il vous plaît.

Réponses:


207

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

(11) Comment ajouter ou supprimer des colonnes d'une table existante dans SQLite.

SQLite a une prise en charge limitée d'ALTER TABLE que vous pouvez utiliser pour ajouter une colonne à la fin d'une table ou pour modifier le nom d'une table. Si vous souhaitez apporter des modifications plus complexes à la structure d'un tableau, vous devrez recréer le tableau. Vous pouvez enregistrer les données existantes dans une table temporaire, supprimer l'ancienne table, créer la nouvelle table, puis recopier les données à partir de la table temporaire.

Par exemple, supposons que vous ayez une table nommée «t1» avec les noms de colonnes «a», «b» et «c» et que vous souhaitiez supprimer la colonne «c» de cette table. Les étapes suivantes illustrent comment cela pourrait être fait:

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
+ Toujours lire la documentation SQLite. Vous remarquerez trop de limitations et de différences dans la grammaire SQL lorsque vous obtenez des erreurs. La documentation SQLite est très simple à comprendre. Ne t'inquiète pas.
AhmetB - Google

2
Vous devez exécuter la commande VACUUM après avoir supprimé les colonnes pour des raisons de sécurité; sans passer l'aspirateur, le fichier de base de données contient toujours les données des colonnes supprimées.
jj1bdx

@ jj1bdx Je ne pense pas qu'il contienne encore les données, mais "l'espace disque inutilisé est ajouté à une" free-list "interne et est réutilisé la prochaine fois que vous insérez des données. L'espace disque n'est pas perdu. Mais il ne l'est pas non plus retourné au système d'exploitation. " comme cité sur le site Web sqlite3.
Guilherme Salomé

Comme j'ai utilisé plusieurs suppressions de colonnes dans une transaction, cela n'a fonctionné que lorsque j'ai supprimé TEMPORARYde CREATE TABLE.
ephemerr

Il y a mon implémentation en utilisant QSqlQuery de Qt: gist.github.com/ephemerr/568d0d41bc389ec78f9fb7d1f015a82a
ephemerr

56

Au lieu de supprimer la table de sauvegarde, renommez-la simplement ...

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
Cela ne fonctionnera pas lorsque vous aurez connecté la clé de foregin à t1.
ephemerr

39

Pour plus de simplicité, pourquoi ne pas créer la table de sauvegarde à partir de l'instruction select?

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

3
Cette approche semble préserver les types de données des colonnes, alors que quelque chose comme la réponse acceptée semble aboutir à ce que toutes les colonnes soient de type TEXT.
Uwe Keim

2
Ces instructions doivent également être incluses dans une transaction.
Georg Schölly

10
Notez que cela ne préserve pas la clé primaire et que sqlite ne prend pas en charge la modification des tables pour ajouter une clé primaire. Donc, si la clé primaire est importante, ce n'est pas ce que vous devriez utiliser
Tim

2
Cela ne préserve pas non plus NOT NULL.
FutureShocked

la réponse acceptée fonctionne bien. Vous êtes censé spécifier les types de données lorsque vous créez la table. Soupir.
John Lord

8

Cette option ne fonctionne que si vous pouvez ouvrir la base de données dans un navigateur de base de données tel que DB Browser pour SQLite .

Dans DB Browser pour SQLite:

  1. Allez dans l'onglet "Structure de la base de données"
  2. Sélectionnez votre table Sélectionnez Modifier la table (juste sous les onglets)
  3. Sélectionnez la colonne que vous souhaitez supprimer
  4. Cliquez sur Supprimer le champ et cliquez sur OK

3

=> Créez une nouvelle table directement avec la requête suivante:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> Insérez maintenant les données dans table_name de existing_table avec la requête suivante:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> Déposez maintenant la table_existante en suivant la requête:

DROP TABLE existing_table;

1

Pour 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

J'ai créé une fonction Python dans laquelle vous entrez la table et la colonne à supprimer en tant qu'arguments:

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

Selon les informations sur les réponses de Duda et MeBigFatGuy, cela ne fonctionnera pas s'il y a une clé étrangère sur la table, mais cela peut être corrigé avec 2 lignes de code (en créant une nouvelle table et pas seulement en renommant la table temporaire)


Qu'est-ce que c? Qu'est-ce que conn? Cette réponse fait trop d'hypothèses sur les variables disponibles de type inconnu.
Ivan Castellanos il y a

0

Au cas où quelqu'un aurait besoin d'une fonction PHP (presque) prête à l'emploi, ce qui suit est basé sur cette réponse :

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

Contrairement à d'autres réponses, le SQL utilisé dans cette approche semble préserver les types de données des colonnes, alors que quelque chose comme la réponse acceptée semble aboutir à ce que toutes les colonnes soient de type TEXT.

Mise à jour 1:

Le SQL utilisé présente l'inconvénient que les autoincrementcolonnes ne sont pas conservées.


0

Juste au cas où ça pourrait aider quelqu'un comme moi.

Sur la base du site officiel et de la réponse acceptée , j'ai créé un code en utilisant C # qui utilise System.Data.SQLite package NuGet .

Ce code préserve également la clé primaire et la clé étrangère .

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

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;

Pour plus d'informations: https://www.techonthenet.com/sqlite/tables/alter_table.php

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.