SQLite tablosundan sütunu sil


114

Bir sorunum var: SQLite veritabanımdan bir sütun silmem gerekiyor. Bu sorguyu yazdım

alter table table_name drop column column_name 

ama bu çalışmıyor. Lütfen bana yardım et.

Yanıtlar:


207

Gönderen: http://www.sqlite.org/faq.html :

(11) SQLite'da mevcut bir tablodan nasıl sütun ekleyebilir veya silebilirim?

SQLite, bir tablonun sonuna sütun eklemek veya bir tablonun adını değiştirmek için kullanabileceğiniz sınırlı ALTER TABLE desteğine sahiptir. Bir tablonun yapısında daha karmaşık değişiklikler yapmak istiyorsanız, tabloyu yeniden oluşturmanız gerekecektir. Mevcut verileri geçici bir tabloya kaydedebilir, eski tabloyu bırakabilir, yeni tabloyu oluşturabilir ve ardından verileri geçici tablodan geri kopyalayabilirsiniz.

Örneğin, "a", "b" ve "c" sütun adlarına sahip "t1" adlı bir tablonuz olduğunu ve bu tablodan "c" sütununu silmek istediğinizi varsayalım. Aşağıdaki adımlar bunun nasıl yapılabileceğini göstermektedir:

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
+ Her zaman SQLite belgelerini okuyun. Hata aldığınızda SQL dilbilgisinde çok fazla sınırlama ve farklılık olduğunu fark edeceksiniz. SQLite Dokümantasyonunun anlaşılması çok kolaydır. Endişelenme.
AhmetB - Google

2
Güvenlik için sütunları kaldırdıktan sonra VACUUM komutunu uygulamanız gerekir; vakumlama olmadan, veritabanı dosyası hala silinmiş sütunların verilerini içerir.
jj1bdx

@ jj1bdx Hala veri içerdiğini düşünmüyorum, ancak "kullanılmayan disk alanı dahili bir" boş listeye "eklenir ve bir dahaki sefere veri eklediğinizde yeniden kullanılır. Disk alanı kaybolmaz. Ama o da değil işletim sistemine geri döndü. " sqlite3 web sitesinden alıntılandığı gibi.
Guilherme Salomé

Bir işlemde birden kolon kaldırma kullanıldığı gibi bu, kaldırıldı sadece çalıştı TEMPORARYden CREATE TABLE.
ephemerr

Qt'nin QSqlQuery'sini kullanan uygulamam var: gist.github.com/ephemerr/568d0d41bc389ec78f9fb7d1f015a82a
ephemerr

56

Yedekleme tablosunu kaldırmak yerine yeniden adlandırın ...

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
Foregin anahtarına bağlıyken çalışmayacaktır t1.
ephemerr

39

Basit olması için, neden select ifadesinden yedek tablo oluşturmuyorsunuz?

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

3
Bu yaklaşım, sütunların veri türlerini koruyor gibi görünürken , kabul edilen yanıt gibi bir şey tüm sütunların türden olmasıyla sonuçlanıyor gibi görünüyor TEXT.
Uwe Keim

2
Bu ifadeler ayrıca bir işleme sarılmalıdır.
Georg Schölly

10
Bunun birincil anahtarı korumadığını ve sqlite'ın birincil anahtar eklemek için tabloları değiştirmeyi desteklemediğini unutmayın. Yani birincil anahtar önemliyse, kullanmanız gereken bu değil
Tim

2
Bu ayrıca NOT NULL'u korumaz.
FutureShocked

kabul edilen cevap iyi çalışıyor. Tabloyu oluştururken veri türlerini belirtmeniz gerekir. İç çekmek.
John Lord

8

Bu seçenek yalnızca, DB'yi bir DB Tarayıcısında aşağıdaki gibi açabiliyorsanız çalışır: , DB'yi SQLite için .

SQLite için DB Tarayıcısında:

  1. "Veritabanı Yapısı" sekmesine gidin
  2. Tablonuzu seçin Tabloyu değiştir'i seçin (sekmelerin hemen altında)
  3. Silmek istediğiniz sütunu seçin
  4. Kaldır alanına tıklayın ve Tamam'a tıklayın

3

=> Aşağıdaki sorgu ile doğrudan yeni bir tablo oluşturun:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> Şimdi verileri mevcut_tablodan tablo_adı içine aşağıdaki sorgu ile ekleyin:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> Şimdi aşağıdaki sorguyu izleyerek mevcut tabloyu bırakın:

DROP TABLE existing_table;

1

SQLite3 c ++ için:

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

Bağımsız değişken olarak kaldırmak için tablo ve sütuna girdiğiniz bir Python işlevi yaptım :

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

Duda'nın ve MeBigFatGuy'un cevapları hakkındaki bilgilere göre, tabloda bir yabancı anahtar varsa bu işe yaramaz, ancak bu 2 satır kodla düzeltilebilir (yeni bir tablo oluşturmak ve sadece geçici tabloyu yeniden adlandırmakla kalmaz)


C nedir? Conn nedir? Bu cevap, bilinmeyen tipte mevcut değişkenlerin çok fazla varsayımını yapar.
Ivan Castellanos

0

Herkesin (neredeyse) kullanıma hazır bir PHP işlevine ihtiyaç duyması durumunda, aşağıdakiler bu yanıta dayanmaktadır :

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

Diğer yanıtların aksine, bu yaklaşımda kullanılan SQL sütunların veri türlerini koruyor gibi görünürken, kabul edilen yanıt gibi bir şey tüm sütunların aynı türden olmasıyla sonuçlanıyor gibi görünmektedir TEXT.

Güncelleme 1:

Kullanılan SQL gibi bir dezavantaja sahiptir autoincrementsütunlar vardır değil korunmuş.


0

Benim gibi birine yardımcı olabilirse diye.

Dayanarak Resmi web sitesi ve Kabul cevap , benim kullandığım bir kod yapılmış C # kullanımları o System.Data.SQLite Nuget paketi.

Bu kod ayrıca Birincil anahtarı ve Yabancı anahtarı da korur .

C # kodunda:

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;

Daha fazla bilgi için: https://www.techonthenet.com/sqlite/tables/alter_table.php

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.