Làm cách nào để xóa hoặc thêm cột trong SQLITE?


239

Tôi muốn xóa hoặc thêm cột trong cơ sở dữ liệu sqlite

Tôi đang sử dụng truy vấn sau để xóa cột.

ALTER TABLE TABLENAME DROP COLUMN COLUMNNAME

Nhưng nó báo lỗi

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

Câu trả lời:


353

BẢNG SQLite

SQLite hỗ trợ một tập hợp con giới hạn của ALTER TABLE. Lệnh ALTER TABLE trong SQLite cho phép người dùng đổi tên bảng hoặc thêm cột mới vào bảng hiện có. Không thể đổi tên một cột, xóa một cột hoặc thêm hoặc xóa các ràng buộc khỏi một bảng.

Bạn có thể:

  1. tạo bảng mới như bảng bạn đang cố gắng thay đổi,
  2. sao chép tất cả dữ liệu
  3. bỏ cái bàn cũ
  4. đổi tên cái mới

47
stackoverflow.com/a/5987838/1578528 đưa ra một ví dụ cơ bản để thực hiện nhiệm vụ.
bikram990

4
Trước khi thực hiện chuỗi này và trong trường hợp có các bảng bên ngoài tham chiếu đến bảng này, người ta phải gọi PRAGMA foreign_keys=OFF. Trong trường hợp này, sau khi thực hiện chuỗi này, người ta phải gọi PRAGMA foreign_keys=ONđể kích hoạt lại khóa ngoại.
PazO

Làm thế nào để bạn cũng sao chép khóa freoign và chỉ số là tốt?
Jonathan

miễn là bạn tạo một bảng mới trước thay vì tạo từ chọn, nó sẽ có tất cả những thứ đó.
John Lord

1
Trong các phiên bản SQLite mới hơn, RENAME COLUMNđược hỗ trợ. 🎉 sqlite.org/releaselog/3_25_0.html
Grogs

46

Tôi đã viết một triển khai Java dựa trên cách được đề xuất của Sqlite để làm điều này:

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

Để lấy cột của bảng, tôi đã sử dụng "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;
}

Tôi thực sự đã viết về nó trên blog của tôi, bạn có thể xem thêm lời giải thích ở đó:

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


1
Điều này là khá chậm mặc dù phải không? cho bảng dữ liệu lớn?
Joran Beasley

2
Sẽ tốt hơn nếu điều này được thực hiện trong một giao dịch, thay vì có khả năng cho phép các mã khác nhìn thấy mọi thứ trong trạng thái chuyển tiếp.
Donal Fellows

Mã này tôi thường chạy khi nâng cấp DB, trong đó mã khác không chạy đồng thời. Bạn có thể tạo một giao dịch và thực hiện tất cả các lệnh trong đó.
Udinic

3
Tôi khá chắc chắn rằng nếu bạn sử dụng giải pháp này, các cột trên bảng kết quả của bạn sẽ hoàn toàn trống - không có thông tin loại, PK, FK, giá trị mặc định, duy nhất hoặc các ràng buộc kiểm tra sẽ vẫn còn. Tất cả nó nhập vào bảng mới là tên cột. Hơn nữa, vì nó không vô hiệu hóa khóa ngoại trước khi chạy, dữ liệu trong các bảng khác cũng có thể bị hỏng.
ACK_stoverflow

4
Ngoài ra, thay vì thực hiện một INSERTtuyên bố, bạn cũng có thể tạo bảng mới bằng cách thực hiện"CREAT TABLE" + tableName + "AS SELECT " + columnsSeperated + " FROM " + tableName + "_old;"
Robert

26

Như những người khác đã chỉ ra

Không thể đổi tên một cột, xóa một cột hoặc thêm hoặc xóa các ràng buộc khỏi một bảng.

nguồn: http://www.sqlite.org/lang_altertable.html

Trong khi bạn luôn có thể tạo một bảng mới và sau đó thả bảng cũ hơn. Tôi sẽ cố gắng giải thích cách giải quyết này bằng một ví dụ.

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       

Bây giờ bạn muốn xóa cột heightkhỏi bảng này.

Tạo một bảng khác gọi là new_person

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

Bây giờ sao chép dữ liệu từ bảng cũ

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>

Bây giờ Thả personbảng và đổi tên new_personthànhperson

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

Vì vậy, bây giờ nếu bạn làm một .schema, bạn sẽ thấy

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

Những gì về tài liệu tham khảo nước ngoài? Oracle sẽ khiếu nại nếu bạn xóa một bảng mà một số bảng khác đang sử dụng.
Leos Literak

6
Tôi có thể nói rằng bạn là một lập trình viên thực sự. Bạn đã hết tên ngay sau john doe và đi thẳng đến "foo bar" :)
msouth

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



4

Như những người khác đã chỉ ra, ALTER TABLEtuyên bố của sqlite không hỗ trợ DROP COLUMNvà công thức chuẩn để làm điều này không bảo tồn các ràng buộc & chỉ số.

Đây là một số mã python để làm điều này một cách khái quát, trong khi vẫn duy trì tất cả các ràng buộc và chỉ số chính.

Vui lòng sao lưu cơ sở dữ liệu của bạn trước khi sử dụng! Hàm này dựa vào việc ghi lại câu lệnh CREATE TABLE ban đầu và có khả năng hơi không an toàn - ví dụ, nó sẽ làm sai nếu một mã định danh chứa dấu phẩy hoặc dấu ngoặc đơn được nhúng.

Nếu bất cứ ai quan tâm đóng góp một cách tốt hơn để phân tích SQL, điều đó sẽ rất tuyệt!

CẬP NHẬT Tôi tìm thấy một cách tốt hơn để phân tích cú pháp bằng cách sử dụngsqlparsegóinguồn mở. Nếu có bất kỳ sự quan tâm nào tôi sẽ đăng nó ở đây, chỉ cần để lại một bình luận yêu cầu nó ...

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

Nó có duy trì các khóa ngoại vào bảng không?
Lasse V. Karlsen

@ LasseV.Karlsen Tôi đã thực hiện một số thử nghiệm và nó sẽ duy trì các ràng buộc khóa ngoài vì những điều này dường như được thi hành theo tên bảng.
spam_egss

Làm thế nào tôi có thể chạy nó từ Java?
Leos Literak

4

Tôi viết lại câu trả lời @Udinic để mã tự động tạo truy vấn tạo bảng . Nó cũng không cần ConnectionSource. Nó cũng phải làm điều này trong một giao dịch .

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

DB Browser cho SQLite cho phép bạn thêm hoặc thả các cột.

Trong giao diện chính, tab Database Structure, bấm vào tên bảng. Một nút Modify Tableđược kích hoạt, mở ra một cửa sổ mới nơi bạn có thể chọn cột / trường và loại bỏ nó.


2

bạn có thể sử dụng Sqlitebrowser. Trong chế độ trình duyệt, đối với cơ sở dữ liệu tương ứng và bảng, dưới cấu trúc cơ sở dữ liệu tab, theo tùy chọn Bảng sửa đổi, có thể xóa cột tương ứng.


2

Tôi đoán những gì bạn muốn làm là di chuyển cơ sở dữ liệu. 'Thả cột không tồn tại trong SQLite. Tuy nhiên, bạn có thể thêm một cột bổ sung bằng cách sử dụng truy vấn bảng ALTER.


1

Bạn có thể sử dụng Quản trị viên SQlite để thay đổi tên cột. Nhấp chuột phải vào tên Bảng và chọn Chỉnh sửa Bảng. Ở đây bạn sẽ tìm thấy cấu trúc bảng và bạn có thể dễ dàng đổi tên nó.


1

Vì SQLite có giới hạn hỗ trợ cho ALTER TABLE, do đó bạn chỉ có thể THÊM cột ở cuối bảng HOẶC THAY ĐỔI TABLE_NAME trong SQLite.

Dưới đây là câu trả lời hay nhất về CÁCH XÓA MÀU SẮC TỪ SQLITE?

truy cập Xóa cột khỏi bảng SQLite


1

Như một sự thay thế:

Nếu bạn có một bảng với lược đồ

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

bạn có thể sử dụng một CREATE TABLE...AStuyên bố như CREATE TABLE person2 AS SELECT id, first_name, last_name, age FROM person;, tức là bỏ qua các cột bạn không muốn. Sau đó thả personbảng gốc và đổi tên bảng mới.

Lưu ý phương pháp này tạo ra một bảng không có KHÓA CHÍNH và không có ràng buộc. Để bảo tồn chúng, hãy sử dụng các phương thức khác được mô tả để tạo bảng mới hoặc sử dụng bảng tạm thời làm trung gian.


1

Câu trả lời này cho một câu hỏi khác được định hướng để sửa đổi một cột, nhưng tôi tin rằng một phần câu trả lời cũng có thể mang lại một cách tiếp cận hữu ích nếu bạn có nhiều cột và không muốn gõ lại hầu hết chúng cho câu lệnh INSERT của bạn:

https://stackoverflow.com/a/10385666

Bạn có thể kết xuất cơ sở dữ liệu của mình như được mô tả trong liên kết ở trên, sau đó lấy câu lệnh "tạo bảng" và mẫu "chèn" từ kết xuất đó, sau đó làm theo các hướng dẫn trong mục Câu hỏi thường gặp về SQLite "Làm cách nào để thêm hoặc xóa các cột khỏi mục hiện có bảng trong SQLite. " (Câu hỏi thường gặp được liên kết ở nơi khác trên trang này.)


Trên thực tế, tôi chỉ nhận ra rằng kết xuất không bao gồm tên cột trong phần chèn theo mặc định. Vì vậy, có thể tốt như nhau chỉ bằng cách sử dụng pragma .schema để lấy tên cột, vì sau đó bạn sẽ cần xóa khai báo kiểu.
burpgrass

1

Triển khai Pythondựa trên thông tin tại http://www.sqlite.org/faq.html#q11 .

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)

Kịch bản này trước tiên tạo bảng tạm thời ngẫu nhiên và chèn dữ liệu của các cột cần thiết ngoại trừ cột sẽ bị loại bỏ. Sau đó khôi phục bảng gốc dựa trên bảng tạm thời và thả bảng tạm thời.


1

Giải pháp của tôi, chỉ cần gọi phương pháp này.

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

Và phương pháp phụ trợ để có được các cột:

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

Phương pháp này không giữ loại colunm, vì vậy tôi đã đăng phiên bản sửa đổi của mã của bạn
Berdimurat Masaliev

1

Tôi đã cải thiện câu trả lời của người dùng2638929 và bây giờ nó có thể bảo tồn loại cột, khóa chính, giá trị mặc định, v.v.

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

Tái bút Tôi đã sử dụng ở đây android.arch.persistence.db.SupportSQLiteDatabase, nhưng bạn có thể dễ dàng sửa đổi nó để sử dụngandroid.database.sqlite.SQLiteDatabase


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

và chỉ cần gọi một phương thức

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

-1

ví dụ để thêm một cột: -

alter table student add column TOB time;

ở đây học sinhtên_bảngTOBcột_name được thêm vào.

Nó đang làm việc và thử nghiệm.


-1

Bây giờ bạn cũng có thể sử dụng trình duyệt DB cho SQLite để thao tác các cột

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.