ลบคอลัมน์จากตาราง 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
Guilherme Salomé

ขณะที่ผมใช้ในการลบคอลัมน์หลายในการทำธุรกรรมนี้ทำงานเฉพาะเมื่อฉันออกจากTEMPORARY CREATE TABLE
ephemerr

มีการใช้งานของฉันโดยใช้ 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
มันจะไม่ทำงานเมื่อคุณมี foregin t1สำคัญที่เชื่อมต่อกับ
ephemerr

39

เพื่อความง่ายทำไมไม่สร้างตารางสำรองจากคำสั่ง select

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

3
วิธีนี้ดูเหมือนว่าจะรักษาชนิดข้อมูลของคอลัมน์ในขณะที่สิ่งที่ต้องการคำตอบที่ได้รับการยอมรับTEXTดูเหมือนว่าจะมีผลในคอลัมน์ทั้งหมดจะเป็นประเภท
Uwe Keim

2
คำสั่งเหล่านี้ควรรวมอยู่ในธุรกรรมด้วย
Georg Schölly

10
โปรดทราบว่าสิ่งนี้ไม่ได้เก็บคีย์หลักไว้และ sqlite ไม่สนับสนุนการแก้ไขตารางเพื่อเพิ่มคีย์หลัก ดังนั้นหากคีย์หลักมีความสำคัญนี่ไม่ใช่สิ่งที่คุณควรใช้
ทิม

2
สิ่งนี้ยังไม่เก็บไม่เป็นโมฆะ
FutureShocked

คำตอบที่ยอมรับทำงานได้ดี คุณควรระบุชนิดข้อมูลเมื่อคุณสร้างตาราง ถอนหายใจ
John Lord

8

ตัวเลือกนี้ใช้งานได้ก็ต่อเมื่อคุณสามารถเปิด DB ใน DB Browser เช่นDB Browser สำหรับ SQLiteSQLite

ใน DB Browser สำหรับ SQLite:

  1. ไปที่แท็บ "โครงสร้างฐานข้อมูล"
  2. เลือกตารางของคุณเลือกปรับเปลี่ยนตาราง (ใต้แท็บ)
  3. เลือกคอลัมน์ที่คุณต้องการลบ
  4. คลิกที่ช่องลบและคลิกตกลง

3

=> สร้างตารางใหม่โดยตรงด้วยแบบสอบถามต่อไปนี้:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> ตอนนี้แทรกข้อมูลลงใน table_name จาก existing_table ด้วยแบบสอบถามต่อไปนี้:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> ตอนนี้วางที่มีอยู่ _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()

ตามข้อมูลในคำตอบของ Duda และ MeBigFatGuy สิ่งนี้จะใช้ไม่ได้หากมีคีย์ต่างประเทศอยู่บนโต๊ะ แต่สามารถแก้ไขได้ด้วยโค้ด 2 บรรทัด (สร้างตารางใหม่ไม่ใช่แค่เปลี่ยนชื่อตารางชั่วคราว)


c คืออะไร? Conn คืออะไร? คำตอบนี้ทำให้มีสมมติฐานเกี่ยวกับตัวแปรที่มีอยู่ในประเภทที่ไม่รู้จักมากเกินไป
Ivan Castellanos

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.