Delete column from SQLite table

SqlSqliteDdl

Sql Problem Overview


I have a problem: I need to delete a column from my SQLite database. I wrote this query

alter table table_name drop column column_name 

but it does not work. Please help me.

Sql Solutions


Solution 1 - Sql

Update: SQLite 2021-03-12 (3.35.0) now supports DROP COLUMN. The FAQ on the website is still outdated.


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

> (11) How do I add or delete columns from an existing table in SQLite. > > SQLite has limited ALTER TABLE support that you can use to add a > column to the end of a table or to change the name of a table. If you > want to make more complex changes in the structure of a table, you > will have to recreate the table. You can save existing data to a > temporary table, drop the old table, create the new table, then copy > the data back in from the temporary table. > > For example, suppose you have a table named "t1" with columns names > "a", "b", and "c" and that you want to delete column "c" from this > table. The following steps illustrate how this could be done: > > 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;

Solution 2 - Sql

Instead of dropping the backup table, just rename it...

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;

Solution 3 - Sql

For simplicity, why not create the backup table from the select statement?

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

Solution 4 - Sql

This option works only if you can open the DB in a DB Browser like DB Browser for SQLite.

In DB Browser for SQLite:

  1. Go to the tab, "Database Structure"
  2. Select you table Select Modify table (just under the tabs)
  3. Select the column you want to delete
  4. Click on Remove field and click OK

Solution 5 - Sql

=>Create a new table directly with the following query:

CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);

=>Now insert the data into table_name from existing_table with the following query:

INSERT INTO table_name (Column_1,Column_2) FROM existing_table;

=>Now drop the existing_table by following query:

DROP TABLE existing_table;

Solution 6 - Sql

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;

For more info: https://www.techonthenet.com/sqlite/tables/alter_table.php

Solution 7 - Sql

I've made a Python function where you enter the table and column to remove as 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()

As per the info on Duda's and MeBigFatGuy's answers this won't work if there is a foreign key on the table, but this can be fixed with 2 lines of code (creating a new table and not just renaming the temporary table)

Solution 8 - Sql

For 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() );	
}

Solution 9 - Sql

In case anyone needs a (nearly) ready-to-use PHP function, the following is based on this answer:

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

In contrast to other answers, the SQL used in this approach seems to preserve the data types of the columns, whereas something like the accepted answer seems to result in all columns to be of type TEXT.

Update 1:

The SQL used has the drawback that autoincrement columns are not preserved.

Solution 10 - Sql

Just in case if it could help someone like me.

Based on the Official website and the Accepted answer, I made a code using C# that uses System.Data.SQLite NuGet package.

This code also preserves the Primary key and Foreign key.

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

Solution 11 - Sql

In Python 3.8... Preserves primary key and column types.

Takes 3 inputs:

  1. a sqlite cursor: db_cur,
  2. table name: t and,
  3. list of columns to junk: columns_to_junk
def removeColumns(db_cur, t, columns_to_junk):

    # Obtain column information
    sql = "PRAGMA table_info(" + t + ")"
    record = query(db_cur, sql)

    # Initialize two strings: one for column names + column types and one just
    # for column names
    cols_w_types = "("
    cols = ""

    # Build the strings, filtering for the column to throw out
    for r in record:
        if r[1] not in columns_to_junk:
            if r[5] == 0:
                cols_w_types += r[1] + " " + r[2] + ","
            if r[5] == 1:
                cols_w_types += r[1] + " " + r[2] + " PRIMARY KEY,"
            cols += r[1] + ","

    # Cut potentially trailing commas
    if cols_w_types[-1] == ",":
        cols_w_types = cols_w_types[:-1]
    else:
        pass

    if cols[-1] == ",":
        cols = cols[:-1]
    else:
        pass

    # Execute SQL
    sql = "CREATE TEMPORARY TABLE xfer " + cols_w_types + ")"
    db_cur.execute(sql)
    sql = "INSERT INTO xfer SELECT " + cols + " FROM " + t
    db_cur.execute(sql)
    sql = "DROP TABLE " + t
    db_cur.execute(sql)
    sql = "CREATE TABLE " + t + cols_w_types + ")"
    db_cur.execute(sql)
    sql = "INSERT INTO " + t + " SELECT " + cols  + " FROM xfer"
    db_cur.execute(sql)

You'll find a reference to a query() function. Just a helper...

Takes two inputs:

  1. sqlite cursor db_cur and,
  2. the query string: query
def query(db_cur, query):

    r = db_cur.execute(query).fetchall()

    return r

Don't forget to include a "commit()"!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsandyView Question on Stackoverflow
Solution 1 - SqlMeBigFatGuyView Answer on Stackoverflow
Solution 2 - SqlDudaView Answer on Stackoverflow
Solution 3 - Sqluser4086833View Answer on Stackoverflow
Solution 4 - SqlMagTunView Answer on Stackoverflow
Solution 5 - Sqluser3317939View Answer on Stackoverflow
Solution 6 - SqlNexus242View Answer on Stackoverflow
Solution 7 - SqlDomView Answer on Stackoverflow
Solution 8 - SqlSunny127View Answer on Stackoverflow
Solution 9 - SqlUwe KeimView Answer on Stackoverflow
Solution 10 - SqlNaveen Kumar VView Answer on Stackoverflow
Solution 11 - Sqluser14375440View Answer on Stackoverflow