SQLITE에서 열을 삭제하거나 추가하는 방법은 무엇입니까?


240

sqlite 데이터베이스에서 열을 삭제하거나 추가하고 싶습니다

다음 쿼리를 사용하여 열을 삭제하고 있습니다.

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

그러나 그것은 오류를 준다

System.Data.SQLite.SQLiteException: SQLite error
near "DROP": syntax error

답변:


353

ALTER TABLE SQLite

SQLite는 ALTER TABLE의 제한된 하위 집합을 지원합니다. SQLite의 ALTER TABLE 명령을 사용하면 테이블 이름을 바꾸거나 기존 테이블에 새 열을 추가 할 수 있습니다. 열 이름을 바꾸거나 열을 제거하거나 테이블에서 제약 조건을 추가하거나 제거 할 수 없습니다.

당신은 할 수 있습니다 :

  1. 변경하려는 테이블로 새 테이블을 만들고
  2. 모든 데이터를 복사
  3. 오래된 테이블을 떨어 뜨리고
  4. 새 이름을 바꿉니다.

47
stackoverflow.com/a/5987838/1578528 은 작업 수행을위한 기본 예를 제공합니다.
bikram990

4
이 순서를 수행하기 전에 그리고이 테이블을 참조하는 외부 테이블이있는 경우 호출해야합니다 PRAGMA foreign_keys=OFF. 이 경우이 순서를 수행 한 후 PRAGMA foreign_keys=ON외래 키를 다시 활성화 하려면 호출해야 합니다.
PazO

freoign 키와 인덱스도 어떻게 복사합니까?
Jonathan

select에서 작성하는 대신 먼저 새 테이블 작성을 수행하는 한 모든 것이 있습니다.
John Lord

1
최신 SQLite 버전에서는 RENAME COLUMN지원됩니다. 🎉 sqlite.org/releaselog/3_25_0.html
Grogs

46

Sqlite의 권장 방법에 따라 Java 구현을 작성했습니다.

private void dropColumn(SQLiteDatabase db,
        ConnectionSource connectionSource,
        String createTableCmd,
        String tableName,
        String[] colsToRemove) throws java.sql.SQLException {

    List<String> updatedTableColumns = getTableColumns(tableName);
    // Remove the columns we don't want anymore from the table's list of columns
    updatedTableColumns.removeAll(Arrays.asList(colsToRemove));

    String columnsSeperated = TextUtils.join(",", updatedTableColumns);

    db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");

    // Creating the table on its new format (no redundant columns)
    db.execSQL(createTableCmd);

    // Populating the table with the data
    db.execSQL("INSERT INTO " + tableName + "(" + columnsSeperated + ") SELECT "
            + columnsSeperated + " FROM " + tableName + "_old;");
    db.execSQL("DROP TABLE " + tableName + "_old;");
}

테이블의 열을 얻기 위해 "PRAGMA table_info"를 사용했습니다.

public List<String> getTableColumns(String tableName) {
    ArrayList<String> columns = new ArrayList<String>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = getDB().rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}

나는 실제로 내 블로그에 그것에 대해 썼습니다. 더 많은 설명을 볼 수 있습니다 :

http://udinic.wordpress.com/2012/05/09/sqlite-drop-column-support/


1
그렇지 않더라도 꽤 느립니다? 빅 데이터 테이블의 경우
Joran Beasley

2
다른 코드가 일시적으로 상태를 볼 수 있도록 잠재적으로 허용하는 것보다 단일 트랜잭션에서이 작업을 수행하는 것이 좋습니다.
Donal Fellows

이 코드는 일반적으로 다른 코드가 동시에 실행되지 않는 DB를 업그레이드 할 때 실행됩니다. 트랜잭션을 작성하고 그 안에있는 모든 명령을 수행 할 수 있습니다.
Udinic

3
이 솔루션을 사용하면 결과 테이블의 열이 완전히 노출되지 않으므로 유형 정보, PK, FK, 기본값, 고유 또는 검사 제약 조건이 유지되지 않습니다. 새 테이블로 가져 오는 것은 열 이름입니다. 또한 실행하기 전에 외래 키를 비활성화하지 않기 때문에 다른 테이블의 데이터도 망칠 수 있습니다.
ACK_stoverflow

4
또는, INSERT진술 을 수행하는 대신"CREAT TABLE" + tableName + "AS SELECT " + columnsSeperated + " FROM " + tableName + "_old;"
로버트

26

다른 사람들이 지적했듯이

열 이름을 바꾸거나 열을 제거하거나 테이블에서 제약 조건을 추가하거나 제거 할 수 없습니다.

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

항상 새 테이블을 만든 다음 이전 테이블을 삭제할 수 있습니다. 이 해결 방법 을 예를 들어 설명하려고합니다 .

sqlite> .schema
CREATE TABLE person(
 id INTEGER PRIMARY KEY, 
 first_name TEXT,
 last_name TEXT, 
 age INTEGER, 
 height INTEGER
);
sqlite> select * from person ; 
id          first_name  last_name   age         height    
----------  ----------  ----------  ----------  ----------
0           john        doe         20          170       
1           foo         bar         25          171       

이제이 height테이블 에서 열을 제거하려고 합니다.

라는 다른 테이블을 만듭니다. new_person

sqlite> CREATE TABLE new_person(
   ...>  id INTEGER PRIMARY KEY, 
   ...>  first_name TEXT, 
   ...>  last_name TEXT, 
   ...>  age INTEGER 
   ...> ) ; 
sqlite> 

이제 이전 테이블의 데이터를 복사하십시오.

sqlite> INSERT INTO new_person
   ...> SELECT id, first_name, last_name, age FROM person ;
sqlite> select * from new_person ;
id          first_name  last_name   age       
----------  ----------  ----------  ----------
0           john        doe         20        
1           foo         bar         25        
sqlite>

이제 person테이블을 삭제 하고 이름 new_person을 바꿉니다 .person

sqlite> DROP TABLE IF EXISTS person ; 
sqlite> ALTER TABLE new_person RENAME TO person ;
sqlite>

그래서 지금 당신이 할 경우 .schema, 당신은 볼 것이다

sqlite>.schema
CREATE TABLE "person"(
 id INTEGER PRIMARY KEY, 
 first_name TEXT, 
 last_name TEXT, 
 age INTEGER 
);

외국 참조는 어떻습니까? 다른 테이블이 사용중인 테이블을 삭제하면 Oracle에서 불만을 표시합니다.
레오 라이트 락

6
나는 당신이 진정한 프로그래머라고 말할 수 있습니다. 당신은 존
도우

1
CREATE TABLE new_person AS SELECT id, first_name, last_name, age FROM person;
Clay



4

다른 사람들이 지적했듯이, sqlite의 ALTER TABLE진술은 지원 하지 않으며DROP COLUMN , 이것을 수행하는 표준 레시피는 제약 및 지수를 보존하지 않습니다.

다음 모든 주요 제약 조건과 색인 을 유지 하면서 일반적 으로이 작업을 수행하는 Python 코드 입니다.

사용하기 전에 데이터베이스를 백업하십시오! 이 함수는 원래 CREATE TABLE 문을 의사 결정하는 데 약간 위험하며 잠재적으로 약간 안전하지 않습니다. 예를 들어 식별자에 쉼표 또는 괄호가 포함되어 있으면 잘못된 작업이 수행됩니다.

누구든지 SQL을 구문 분석하는 더 나은 방법을 제공하고자한다면 좋을 것입니다!

업데이트 오픈 소스sqlparse패키지를사용하여 구문 분석하는 더 좋은 방법을 찾았습니다. 관심이 있으시면 여기에 게시 할 것입니다.

import re
import random

def DROP_COLUMN(db, table, column):
    columns = [ c[1] for c in db.execute("PRAGMA table_info(%s)" % table) ]
    columns = [ c for c in columns if c != column ]
    sql = db.execute("SELECT sql from sqlite_master where name = '%s'" 
        % table).fetchone()[0]
    sql = format(sql)
    lines = sql.splitlines()
    findcol = r'\b%s\b' % column
    keeplines = [ line for line in lines if not re.search(findcol, line) ]
    create = '\n'.join(keeplines)
    create = re.sub(r',(\s*\))', r'\1', create)
    temp = 'tmp%d' % random.randint(1e8, 1e9)
    db.execute("ALTER TABLE %(old)s RENAME TO %(new)s" % { 
        'old': table, 'new': temp })
    db.execute(create)
    db.execute("""
        INSERT INTO %(new)s ( %(columns)s ) 
        SELECT %(columns)s FROM %(old)s
    """ % { 
        'old': temp,
        'new': table,
        'columns': ', '.join(columns)
    })  
    db.execute("DROP TABLE %s" % temp)

def format(sql):
    sql = sql.replace(",", ",\n")
    sql = sql.replace("(", "(\n")
    sql = sql.replace(")", "\n)")
    return sql

테이블에 대한 외래 키도 유지합니까?
Lasse V. Karlsen

@ LasseV.Karlsen 몇 가지 테스트를 수행했으며 외래 키 제약 조건을 테이블 이름으로 적용하는 것처럼 유지해야합니다.
spam_eggs

Java에서 어떻게 실행할 수 있습니까?
Leos Literak

4

코드가 테이블 생성 쿼리를 자동으로 생성하도록 @Udinic 응답을 다시 작성했습니다 . 또한 필요하지 않습니다 . 또한 트랜잭션 내에서이 작업을 수행해야 합니다 .ConnectionSource

public static String getOneTableDbSchema(SQLiteDatabase db, String tableName) {
    Cursor c = db.rawQuery(
            "SELECT * FROM `sqlite_master` WHERE `type` = 'table' AND `name` = '" + tableName + "'", null);
    String result = null;
    if (c.moveToFirst()) {
        result = c.getString(c.getColumnIndex("sql"));
    }
    c.close();
    return result;
}

public List<String> getTableColumns(SQLiteDatabase db, String tableName) {
    ArrayList<String> columns = new ArrayList<>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = db.rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}

private void dropColumn(SQLiteDatabase db, String tableName, String[] columnsToRemove) {
    db.beginTransaction();
    try {
        List<String> columnNamesWithoutRemovedOnes = getTableColumns(db, tableName);
        // Remove the columns we don't want anymore from the table's list of columns
        columnNamesWithoutRemovedOnes.removeAll(Arrays.asList(columnsToRemove));

        String newColumnNamesSeparated = TextUtils.join(" , ", columnNamesWithoutRemovedOnes);
        String sql = getOneTableDbSchema(db, tableName);
        // Extract the SQL query that contains only columns
        String oldColumnsSql = sql.substring(sql.indexOf("(")+1, sql.lastIndexOf(")"));

        db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
        db.execSQL("CREATE TABLE `" + tableName + "` (" + getSqlWithoutRemovedColumns(oldColumnsSql, columnsToRemove)+ ");");
        db.execSQL("INSERT INTO " + tableName + "(" + newColumnNamesSeparated + ") SELECT " + newColumnNamesSeparated + " FROM " + tableName + "_old;");
        db.execSQL("DROP TABLE " + tableName + "_old;");
        db.setTransactionSuccessful();
    } catch {
        //Error in between database transaction 
    } finally {
        db.endTransaction();
    }


}

3

SQLite 용 DB 브라우저를 사용하면 열을 추가하거나 삭제할 수 있습니다.

기본보기 탭 Database Structure에서 테이블 이름을 클릭하십시오. 버튼 Modify Table이 활성화되어 열 / 필드를 선택하고 제거 할 수있는 새 창이 열립니다.


2

Sqlitebrowser를 사용할 수 있습니다. 브라우저 모드에서 각 데이터베이스 및 테이블의 탭-데이터베이스 구조에서 테이블 수정 옵션에 따라 각 열을 제거 할 수 있습니다.


2

user2638929 답변 을 개선 했으며 이제 열 유형, 기본 키, 기본값 등을 유지할 수 있습니다.

private static void dropColumn(SupportSQLiteDatabase database, String tableName, List<String> columnsToRemove){
    List<String> columnNames = new ArrayList<>();
    List<String> columnNamesWithType = new ArrayList<>();
    List<String> primaryKeys = new ArrayList<>();
    String query = "pragma table_info(" + tableName + ");";
    Cursor cursor = database.query(query);
    while (cursor.moveToNext()){
        String columnName = cursor.getString(cursor.getColumnIndex("name"));

        if (columnsToRemove.contains(columnName)){
            continue;
        }

        String columnType = cursor.getString(cursor.getColumnIndex("type"));
        boolean isNotNull = cursor.getInt(cursor.getColumnIndex("notnull")) == 1;
        boolean isPk = cursor.getInt(cursor.getColumnIndex("pk")) == 1;

        columnNames.add(columnName);
        String tmp = "`" + columnName + "` " + columnType + " ";
        if (isNotNull){
            tmp += " NOT NULL ";
        }

        int defaultValueType = cursor.getType(cursor.getColumnIndex("dflt_value"));
        if (defaultValueType == Cursor.FIELD_TYPE_STRING){
            tmp += " DEFAULT " + "\"" + cursor.getString(cursor.getColumnIndex("dflt_value")) + "\" ";
        }else if(defaultValueType == Cursor.FIELD_TYPE_INTEGER){
            tmp += " DEFAULT " + cursor.getInt(cursor.getColumnIndex("dflt_value")) + " ";
        }else if (defaultValueType == Cursor.FIELD_TYPE_FLOAT){
            tmp += " DEFAULT " + cursor.getFloat(cursor.getColumnIndex("dflt_value")) + " ";
        }
        columnNamesWithType.add(tmp);
        if (isPk){
            primaryKeys.add("`" + columnName + "`");
        }
    }
    cursor.close();

    String columnNamesSeparated = TextUtils.join(", ", columnNames);
    if (primaryKeys.size() > 0){
        columnNamesWithType.add("PRIMARY KEY("+ TextUtils.join(", ", primaryKeys) +")");
    }
    String columnNamesWithTypeSeparated = TextUtils.join(", ", columnNamesWithType);

    database.beginTransaction();
    try {
        database.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
        database.execSQL("CREATE TABLE " + tableName + " (" + columnNamesWithTypeSeparated + ");");
        database.execSQL("INSERT INTO " + tableName + " (" + columnNamesSeparated + ") SELECT "
                + columnNamesSeparated + " FROM " + tableName + "_old;");
        database.execSQL("DROP TABLE " + tableName + "_old;");
        database.setTransactionSuccessful();
    }finally {
        database.endTransaction();
    }
}

추신. 나는 여기 android.arch.persistence.db.SupportSQLiteDatabase에 사용 했지만 쉽게 사용할 수 있도록 수정할 수 있습니다.android.database.sqlite.SQLiteDatabase


2

당신이 원하는 것은 데이터베이스 마이그레이션입니다. SQLite에는 열을 '삭제'하지 않습니다. 그러나 ALTER 테이블 쿼리를 사용하여 추가 열을 추가 할 수 있습니다.


1

열 이름을 변경하기 위해 SQlite 관리자를 사용할 수 있습니다. 테이블 이름을 마우스 오른쪽 버튼으로 클릭하고 테이블 편집을 선택하면 테이블 구조를 찾을 수 있으며 쉽게 이름을 바꿀 수 있습니다.


1

SQLite는 ALTER TABLE에 대한 지원이 제한되어 있으므로 SQLite의 테이블 끝 또는 CHANGE TABLE_NAME에서만 열을 추가 할 수 있습니다.

다음은 SQLITE에서 컬럼을 삭제하는 방법에 대한 가장 좋은 답변입니다.

SQLite 테이블에서 열 삭제 방문


1

대안으로 :

스키마가있는 테이블이있는 경우

CREATE TABLE person(
  id INTEGER PRIMARY KEY,
  first_name TEXT,
  last_name TEXT,
  age INTEGER,
  height INTEGER
);

당신은 원하지 않는 열을 제외하고 CREATE TABLE...AS같은 문장을 사용할 수 있습니다 CREATE TABLE person2 AS SELECT id, first_name, last_name, age FROM person;. 그런 다음 원래 person테이블을 삭제하고 새 테이블의 이름을 바꾸십시오.

이 메소드는 테이블에 PRIMARY KEY가없고 제한 조건이없는 테이블을 생성합니다. 이를 유지하려면 설명 된 다른 방법을 사용하여 새 테이블을 작성하거나 임시 테이블 을 중간으로 사용하십시오.


1

다른 질문에 대한이 답변은 열 을 수정 하는 데 중점을두고 있지만 많은 열이 있고 INSERT 문에 대해 대부분 직접 입력하지 않으려는 경우 답변의 일부가 유용한 접근 방식을 제공 할 수 있다고 생각합니다.

https://stackoverflow.com/a/10385666

위의 링크에 설명 된대로 데이터베이스를 덤프 한 다음 해당 덤프에서 "테이블 작성"명령문 및 "삽입"템플리트를 가져온 다음 SQLite FAQ 항목 "기존에서 열을 추가 또는 삭제하는 방법"의 지시 사항을 따르십시오. SQLite의 테이블. " (FAQ는이 페이지의 다른 곳에 링크되어 있습니다.)


실제로, 나는 덤프가 기본적으로 삽입에 열 이름을 포함하지 않는다는 것을 깨달았습니다. 따라서 .schema pragma를 사용하여 열 이름을 가져 오는 것만 큼 똑같이 좋습니다. 유형 선언을 어느 쪽이든 삭제해야하기 때문입니다.
burpgrass

1

http://www.sqlite.org/faq.html#q11의Python 정보를 기반으로 한 구현 .

import sqlite3 as db
import random
import string

QUERY_TEMPLATE_GET_COLUMNS = "PRAGMA table_info(@table_name)"
QUERY_TEMPLATE_DROP_COLUMN = """
  BEGIN TRANSACTION;
  CREATE TEMPORARY TABLE @tmp_table(@columns_to_keep);
  INSERT INTO @tmp_table SELECT @columns_to_keep FROM @table_name;
  DROP TABLE @table_name;
  CREATE TABLE @table_name(@columns_to_keep);
  INSERT INTO @table_name SELECT @columns_to_keep FROM @tmp_table;
  DROP TABLE @tmp_table;
  COMMIT;
"""

def drop_column(db_file, table_name, column_name):
    con = db.connect(db_file)
    QUERY_GET_COLUMNS = QUERY_TEMPLATE_GET_COLUMNS.replace("@table_name", table_name)
    query_res = con.execute(QUERY_GET_COLUMNS).fetchall()
    columns_list_to_keep = [i[1] for i in query_res if i[1] != column_name]
    columns_to_keep = ",".join(columns_list_to_keep)
    tmp_table = "tmp_%s" % "".join(random.sample(string.ascii_lowercase, 10))
    QUERY_DROP_COLUMN = QUERY_TEMPLATE_DROP_COLUMN.replace("@table_name", table_name)\
        .replace("@tmp_table", tmp_table).replace("@columns_to_keep", columns_to_keep)
    con.executescript(QUERY_DROP_COLUMN)
    con.close()

drop_column(DB_FILE, TABLE_NAME, COLUMN_NAME)

이 스크립트는 먼저 임의의 임시 테이블을 만들고 삭제 될 열을 제외하고 필요한 열의 데이터 만 삽입합니다. 그런 다음 임시 테이블을 기반으로 원래 테이블을 복원하고 임시 테이블을 삭제합니다.


1

내 솔루션은이 방법을 호출하면됩니다.

public static void dropColumn(SQLiteDatabase db, String tableName, String[] columnsToRemove) throws java.sql.SQLException {
    List<String> updatedTableColumns = getTableColumns(db, tableName);
    updatedTableColumns.removeAll(Arrays.asList(columnsToRemove));
    String columnsSeperated = TextUtils.join(",", updatedTableColumns);

    db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
    db.execSQL("CREATE TABLE " + tableName + " (" + columnsSeperated + ");");
    db.execSQL("INSERT INTO " + tableName + "(" + columnsSeperated + ") SELECT "
            + columnsSeperated + " FROM " + tableName + "_old;");
    db.execSQL("DROP TABLE " + tableName + "_old;");
}

그리고 열을 얻는 보조 방법 :

public static List<String> getTableColumns(SQLiteDatabase db, String tableName) {
    ArrayList<String> columns = new ArrayList<>();
    String cmd = "pragma table_info(" + tableName + ");";
    Cursor cur = db.rawQuery(cmd, null);

    while (cur.moveToNext()) {
        columns.add(cur.getString(cur.getColumnIndex("name")));
    }
    cur.close();

    return columns;
}

내가 게시 된 때문에이 방법은 colunm 유형을 유지하지 않습니다 버전 수정 코드의
Berdimurat Masaliev

0
public void DeleteColFromTable(String DbName, String TableName, String ColName){
    SQLiteDatabase db = openOrCreateDatabase(""+DbName+"", Context.MODE_PRIVATE, null);
    db.execSQL("CREATE TABLE IF NOT EXISTS "+TableName+"(1x00dff);");
    Cursor c = db.rawQuery("PRAGMA table_info("+TableName+")", null);
    if (c.getCount() == 0) {

    } else {
        String columns1 = "";
        String columns2 = "";
        while (c.moveToNext()) {
            if (c.getString(1).equals(ColName)) {
            } else {
                columns1 = columns1 + ", " + c.getString(1) + " " + c.getString(2);
                columns2 = columns2 + ", " + c.getString(1);
            }
            if (c.isLast()) {
                db.execSQL("CREATE TABLE IF NOT EXISTS DataBackup (" + columns1 + ");");
                db.execSQL("INSERT INTO DataBackup SELECT " + columns2 + " FROM "+TableName+";");
                db.execSQL("DROP TABLE "+TableName+"");
                db.execSQL("ALTER TABLE DataBackup RENAME TO "+TableName+";");
            }
        }
    }
}

그냥 메서드를 호출

DeleteColFromTable("Database name","Table name","Col name which want to delete");


-2

열을 추가하는 예 :-

alter table student add column TOB time;

여기서 studenttable_name 이고 TOB 는 추가 할 column_name 입니다.

작동하고 테스트되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.