Spalte aus SQLite-Tabelle löschen


114

Ich habe ein Problem: Ich muss eine Spalte aus meiner SQLite-Datenbank löschen. Ich habe diese Abfrage geschrieben

alter table table_name drop column column_name 

aber es funktioniert nicht. Bitte hilf mir.

Antworten:


206

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

(11) Wie füge ich Spalten zu einer vorhandenen Tabelle in SQLite hinzu oder lösche sie?

SQLite bietet nur eingeschränkte Unterstützung für ALTER TABLE, mit der Sie eine Spalte am Ende einer Tabelle hinzufügen oder den Namen einer Tabelle ändern können. Wenn Sie komplexere Änderungen an der Struktur einer Tabelle vornehmen möchten, müssen Sie die Tabelle neu erstellen. Sie können vorhandene Daten in einer temporären Tabelle speichern, die alte Tabelle löschen, die neue Tabelle erstellen und die Daten dann wieder aus der temporären Tabelle kopieren.

Angenommen, Sie haben eine Tabelle mit dem Namen "t1" mit den Spaltennamen "a", "b" und "c" und möchten die Spalte "c" aus dieser Tabelle löschen. Die folgenden Schritte veranschaulichen, wie dies getan werden kann:

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
+ Lesen Sie immer die SQLite-Dokumentation. Sie werden zu viele Einschränkungen und Unterschiede in der SQL-Grammatik bemerken, wenn Sie Fehler erhalten. Die SQLite-Dokumentation ist sehr einfach zu verstehen. Mach dir keine Sorgen.
AhmetB - Google

2
Sie müssen den Befehl VACUUM ausführen, nachdem Sie die Spalten aus Sicherheitsgründen entfernt haben. Ohne Staubsaugen enthält die Datenbankdatei weiterhin die Daten gelöschter Spalten.
jj1bdx

@ jj1bdx Ich glaube nicht, dass es noch die Daten enthält, aber "der nicht verwendete Speicherplatz wird einer internen" freien Liste "hinzugefügt und beim nächsten Einfügen von Daten wiederverwendet. Der Speicherplatz geht nicht verloren. Aber er geht auch nicht verloren zum Betriebssystem zurückgekehrt. " wie von der sqlite3-Website zitiert.
Guilherme Salomé

Da ich mehrere Spalte Umzüge in einer Transaktion verwendet , arbeitete dies nur , wenn ich entfernt TEMPORARYvon CREATE TABLE.
Ephemerr

Es gibt meine Implementierung mit Qts QSqlQuery: gist.github.com/ephemerr/568d0d41bc389ec78f9fb7d1f015a82a
Ephemerr

55

Anstatt die Sicherungstabelle zu löschen, benennen Sie sie einfach um ...

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
Es funktioniert nicht, wenn Sie den Foregin-Schlüssel angeschlossen haben t1.
Ephemerr

39

Erstellen Sie der Einfachheit halber die Sicherungstabelle aus der select-Anweisung.

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

3
Dieser Ansatz scheint die Datentypen der Spalten beizubehalten, während so etwas wie die akzeptierte Antwort dazu zu führen scheint, dass alle Spalten vom Typ sind TEXT.
Uwe Keim

2
Diese Anweisung sollte auch in eine Transaktion eingeschlossen werden.
Georg Schölly

10
Beachten Sie, dass der Primärschlüssel dadurch nicht erhalten bleibt und SQLite das Ändern von Tabellen zum Hinzufügen von Primärschlüsseln nicht unterstützt. Wenn also der Primärschlüssel wichtig ist, sollten Sie ihn nicht verwenden
Tim

2
Dies bewahrt auch NICHT NOT NULL.
FutureShocked

Die akzeptierte Antwort funktioniert einwandfrei. Sie sollten die Datentypen beim Erstellen der Tabelle angeben. Seufzer.
John Lord

7

Diese Option funktioniert nur, wenn Sie die Datenbank in einem DB-Browser wie DB Browser for SQLite öffnen können .

Im DB Browser für SQLite:

  1. Gehen Sie zur Registerkarte "Datenbankstruktur".
  2. Wählen Sie Ihre Tabelle aus. Wählen Sie Tabelle ändern (direkt unter den Registerkarten).
  3. Wählen Sie die Spalte aus, die Sie löschen möchten
  4. Klicken Sie auf das Feld Entfernen und dann auf OK

3

=> Erstellen Sie eine neue Tabelle direkt mit der folgenden Abfrage:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> Fügen Sie nun die Daten mit der folgenden Abfrage aus der vorhandenen Tabelle in den Tabellennamen ein:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> Löschen Sie nun die vorhandene Tabelle durch folgende Abfrage:

DROP TABLE existing_table;

2

Ich habe eine Python- Funktion erstellt, in der Sie die Tabelle und die Spalte eingeben, die als Argumente entfernt werden sollen:

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

Gemäß den Informationen zu den Antworten von Duda und MeBigFatGuy funktioniert dies nicht, wenn ein Fremdschlüssel in der Tabelle vorhanden ist. Dies kann jedoch mit zwei Codezeilen behoben werden (Erstellen einer neuen Tabelle und nicht nur Umbenennen der temporären Tabelle).


1

Für 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() );   
}

0

Falls jemand eine (fast) einsatzbereite PHP-Funktion benötigt, basiert Folgendes auf dieser Antwort :

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

Im Gegensatz zu anderen Antworten scheint die in diesem Ansatz verwendete SQL die Datentypen der Spalten beizubehalten, während so etwas wie die akzeptierte Antwort dazu führt, dass alle Spalten vom Typ sind TEXT.

Update 1:

Das verwendete SQL hat den Nachteil, dass autoincrementSpalten nicht erhalten bleiben.


0

Nur für den Fall, dass es jemandem wie mir helfen könnte.

Basierend auf der offiziellen Website und der akzeptierten Antwort habe ich mit C # einen Code erstellt , der das System.Data.SQLite NuGet-Paket verwendet.

Dieser Code behält auch den Primärschlüssel und den Fremdschlüssel bei .

CODE in 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;

Für weitere Informationen: https://www.techonthenet.com/sqlite/tables/alter_table.php

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.