SQLite 테이블에서 열 삭제


114

문제가 있습니다. SQLite 데이터베이스에서 열을 삭제해야합니다. 이 쿼리를 작성했습니다.

alter table table_name drop column column_name 

하지만 작동하지 않습니다. 제발 도와주세요.

답변:


207

출처 : http://www.sqlite.org/faq.html :

(11) SQLite의 기존 테이블에서 열을 추가하거나 삭제하는 방법은 무엇입니까?

SQLite는 테이블 끝에 열을 추가하거나 테이블 이름을 변경하는 데 사용할 수있는 제한된 ALTER TABLE 지원을 제공합니다. 테이블 구조를 더 복잡하게 변경하려면 테이블을 다시 만들어야합니다. 기존 데이터를 임시 테이블에 저장하고, 이전 테이블을 삭제하고, 새 테이블을 만든 다음 임시 테이블에서 데이터를 다시 복사 할 수 있습니다.

예를 들어 열 이름이 "a", "b"및 "c"인 "t1"이라는 테이블이 있고이 테이블에서 "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

Qt의 QSqlQuery를 사용한 구현이 있습니다 : 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 2011

10
이것은 기본 키를 보존하지 않으며 sqlite는 기본 키를 추가하기위한 테이블 변경을 지원하지 않습니다. 따라서 기본 키가 중요하다면 사용해야하는 것이 아닙니다
Tim

2
이것은 또한 NOT NULL을 보존하지 않습니다.
FutureShocked

받아 들여진 대답은 잘 작동합니다. 테이블을 생성 할 때 데이터 유형을 지정해야합니다. 한숨.
John Lord

8

이 옵션은 DB Browser for SQLite 와 같은 DB Browser 에서 DB를 열 수있는 경우에만 작동합니다 .

SQLite 용 DB 브라우저에서 :

  1. "데이터베이스 구조"탭으로 이동합니다.
  2. 테이블 선택 테이블 수정 선택 (탭 바로 아래)
  3. 삭제할 열을 선택하십시오.
  4. 필드 제거를 클릭하고 확인을 클릭하십시오.

3

=> 다음 쿼리를 사용하여 직접 새 테이블을 만듭니다.

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=> 이제 다음 쿼리를 사용하여 existing_table에서 table_name에 데이터를 삽입합니다.

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=> 이제 다음 쿼리로 existing_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의 답변에 대한 정보에 따르면 테이블에 외래 키가 있으면 작동하지 않지만 두 줄의 코드로 해결할 수 있습니다 (임시 테이블 이름을 바꾸는 것이 아니라 새 테이블 만들기)


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

나 같은 사람을 도울 수 있다면.

공식 웹 사이트Accepted answer를 기반으로 System.Data.SQLite NuGet 패키지 를 사용 하는 C # 을 사용 하여 코드를 만들었습니다 .

이 코드는 기본 키외래 키 도 보존합니다 .

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.