Kami baru-baru ini perlu menambahkan kolom ke beberapa tabel database SQLite yang ada. Ini bisa dilakukan dengan ALTER TABLE ADD COLUMN
. Tentu saja, jika tabel sudah diubah, kita ingin membiarkannya. Sayangnya, SQLite tidak mendukung IF NOT EXISTS
klausul tentang ALTER TABLE
.
Solusi kami saat ini adalah dengan mengeksekusi pernyataan ALTER TABLE dan mengabaikan kesalahan "nama kolom duplikat", seperti contoh Python ini (tetapi dalam C ++).
Namun, pendekatan biasa kami untuk menyiapkan skema database adalah memiliki skrip .sql yang berisi CREATE TABLE IF NOT EXISTS
dan CREATE INDEX IF NOT EXISTS
pernyataan, yang dapat dieksekusi menggunakan sqlite3_exec
atau sqlite3
alat baris perintah. Kami tidak dapat memasukkan ALTER TABLE
file skrip ini karena jika pernyataan itu gagal, apa pun setelah itu tidak akan dijalankan.
Saya ingin memiliki definisi tabel di satu tempat dan tidak memisahkan antara file .sql dan .cpp. Apakah ada cara untuk menulis solusi ALTER TABLE ADD COLUMN IF NOT EXISTS
di SQLite SQL murni?
sumber
user_version
? Saya berasumsi nol, tapi alangkah baiknya melihat itu didokumentasikan.IF
danALTER TABLE
tidak bersyarat? Apa yang Anda maksud dengan "99% SQL murni"?user_version
, tampaknya 0, tetapi sebenarnya ini adalah nilai yang ditentukan pengguna, jadi Anda dapat membuat nilai awal Anda sendiri.user_version
nilai awal relevan ketika Anda memiliki database yang sudah ada, dan Anda belum pernah menggunakanuser_version
sebelumnya, tetapi Anda ingin mulai menggunakannya, jadi Anda perlu menganggap sqlite menyetelnya ke nilai awal tertentu.Salah satu solusinya adalah dengan membuat kolom dan menangkap pengecualian / kesalahan yang muncul jika kolom tersebut sudah ada. Saat menambahkan beberapa kolom, tambahkan dalam pernyataan ALTER TABLE terpisah sehingga satu duplikat tidak mencegah yang lain dibuat.
Dengan sqlite-net , kami melakukan sesuatu seperti ini. Ini tidak sempurna, karena kami tidak dapat membedakan kesalahan sqlite duplikat dari kesalahan sqlite lainnya.
Dictionary<string, string> columnNameToAddColumnSql = new Dictionary<string, string> { { "Column1", "ALTER TABLE MyTable ADD COLUMN Column1 INTEGER" }, { "Column2", "ALTER TABLE MyTable ADD COLUMN Column2 TEXT" } }; foreach (var pair in columnNameToAddColumnSql) { string columnName = pair.Key; string sql = pair.Value; try { this.DB.ExecuteNonQuery(sql); } catch (System.Data.SQLite.SQLiteException e) { _log.Warn(e, string.Format("Failed to create column [{0}]. Most likely it already exists, which is fine.", columnName)); } }
sumber
SQLite juga mendukung pernyataan pragma yang disebut "table_info" yang mengembalikan satu baris per kolom dalam tabel dengan nama kolom (dan informasi lain tentang kolom). Anda dapat menggunakan ini dalam kueri untuk memeriksa kolom yang hilang, dan jika tidak ada, ubah tabel.
PRAGMA table_info(foo_table_name)
http://www.sqlite.org/pragma.html#pragma_table_info
sumber
Jika Anda melakukan ini dalam pernyataan pemutakhiran DB, mungkin cara paling sederhana adalah dengan hanya menangkap pengecualian yang dilemparkan jika Anda mencoba untuk menambahkan bidang yang mungkin sudah ada.
try { db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN foo TEXT default null"); } catch (SQLiteException ex) { Log.w(TAG, "Altering " + TABLE_NAME + ": " + ex.getMessage()); }
sumber
threre adalah metode PRAGMA adalah table_info (nama_tabel), ia mengembalikan semua informasi tabel.
Berikut ini implementasi cara menggunakannya agar kolom centang ada atau tidak,
public boolean isColumnExists (String table, String column) { boolean isExists = false Cursor cursor; try { cursor = db.rawQuery("PRAGMA table_info("+ table +")", null); if (cursor != null) { while (cursor.moveToNext()) { String name = cursor.getString(cursor.getColumnIndex("name")); if (column.equalsIgnoreCase(name)) { isExists = true; break; } } } } finally { if (cursor != null && !cursor.isClose()) cursor.close(); } return isExists; }
Anda juga dapat menggunakan kueri ini tanpa menggunakan loop,
cursor = db.rawQuery("PRAGMA table_info("+ table +") where name = " + column, null);
sumber
we give no shit about performance
:)).SELECT * FROM pragma_table_info(...)
(perhatikan SELECT dan garis bawahi antara pragma dan info tabel). Tidak yakin versi apa yang sebenarnya mereka tambahkan, ini tidak berfungsi pada 3.16.0 tetapi berfungsi pada 3.22.0.Bagi mereka yang ingin menggunakan
pragma table_info()
result sebagai bagian dari SQL yang lebih besar.select count(*) from pragma_table_info('<table_name>') where name='<column_name>';
Bagian utama adalah untuk menggunakan
pragma_table_info('<table_name>')
bukanpragma table_info('<table_name>')
.Jawaban ini terinspirasi dari balasan @Robert Hawkey. Alasan saya mempostingnya sebagai jawaban baru adalah saya tidak memiliki reputasi yang cukup untuk mempostingnya sebagai komentar.
sumber
Saya datang dengan pertanyaan ini
SELECT CASE (SELECT count(*) FROM pragma_table_info(''product'') c WHERE c.name = ''purchaseCopy'') WHEN 0 THEN ALTER TABLE product ADD purchaseCopy BLOB END
sumber
Jika Anda mengalami masalah ini di flex / adobe air dan menemukan diri Anda di sini terlebih dahulu, saya telah menemukan solusinya, dan telah mempostingnya pada pertanyaan terkait: TAMBAHKAN KOLOM ke sqlite db JIKA TIDAK ADA - flex / air sqlite?
Komentar saya di sini: https://stackoverflow.com/a/24928437/2678219
sumber
Saya mengambil jawaban di atas di C # /. Net, dan menulis ulang untuk Qt / C ++, tidak terlalu banyak berubah, tetapi saya ingin meninggalkannya di sini untuk siapa pun di masa depan yang mencari jawaban 'ish' C ++.
bool MainWindow::isColumnExisting(QString &table, QString &columnName){ QSqlQuery q; try { if(q.exec("PRAGMA table_info("+ table +")")) while (q.next()) { QString name = q.value("name").toString(); if (columnName.toLower() == name.toLower()) return true; } } catch(exception){ return false; } return false; }
sumber
Sebagai alternatif, Anda dapat menggunakan pernyataan CASE-WHEN TSQL yang dikombinasikan dengan pragma_table_info untuk mengetahui apakah ada kolom:
select case(CNT) WHEN 0 then printf('not found') WHEN 1 then printf('found') END FROM (SELECT COUNT(*) AS CNT FROM pragma_table_info('myTableName') WHERE name='columnToCheck')
sumber
Ini solusi saya, tetapi dengan python (saya mencoba dan gagal menemukan posting apa pun tentang topik yang terkait dengan python):
# modify table for legacy version which did not have leave type and leave time columns of rings3 table. sql = 'PRAGMA table_info(rings3)' # get table info. returns an array of columns. result = inquire (sql) # call homemade function to execute the inquiry if len(result)<= 6: # if there are not enough columns add the leave type and leave time columns sql = 'ALTER table rings3 ADD COLUMN leave_type varchar' commit(sql) # call homemade function to execute sql sql = 'ALTER table rings3 ADD COLUMN leave_time varchar' commit(sql)
Saya menggunakan PRAGMA untuk mendapatkan informasi tabel. Ini mengembalikan array multidimensi yang penuh dengan informasi tentang kolom - satu array per kolom. Saya menghitung jumlah array untuk mendapatkan jumlah kolom. Jika kolom tidak cukup, maka saya menambahkan kolom menggunakan perintah ALTER TABLE.
sumber
Semua jawaban ini baik-baik saja jika Anda mengeksekusi satu baris dalam satu waktu. Namun, pertanyaan aslinya adalah memasukkan skrip sql yang akan dieksekusi oleh satu db yang dieksekusi dan semua solusi (seperti memeriksa untuk melihat apakah kolom ada di sana sebelumnya) akan memerlukan program pelaksana baik memiliki pengetahuan tentang tabel apa dan kolom sedang diubah / ditambahkan atau lakukan pra-pemrosesan dan parsing dari skrip input untuk menentukan informasi ini. Biasanya Anda tidak akan menjalankan ini secara realtime atau sering. Jadi gagasan menangkap pengecualian dapat diterima dan kemudian dilanjutkan. Di situlah letak masalahnya ... bagaimana melanjutkan. Untungnya pesan kesalahan memberi kita semua informasi yang kita butuhkan untuk melakukan ini. Idenya adalah untuk mengeksekusi sql jika pengecualian pada panggilan tabel alter, kita dapat menemukan baris tabel alter di sql dan mengembalikan baris yang tersisa dan mengeksekusi sampai berhasil atau tidak ada lagi baris tabel perubahan yang cocok dapat ditemukan. Inilah beberapa contoh kode di mana kita memiliki skrip sql dalam sebuah array. Kami mengulang array yang mengeksekusi setiap skrip. Kami menyebutnya dua kali untuk membuat perintah alter table gagal tetapi program berhasil karena kami menghapus perintah alter table dari sql dan mengeksekusi ulang kode yang diperbarui.
#!/bin/sh # the next line restarts using wish \ exec /opt/usr8.6.3/bin/tclsh8.6 "$0" ${1+"$@"} foreach pkg {sqlite3 } { if { [ catch {package require {*}$pkg } err ] != 0 } { puts stderr "Unable to find package $pkg\n$err\n ... adjust your auto_path!"; } } array set sqlArray { 1 { CREATE TABLE IF NOT EXISTS Notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, name text, note text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ); CREATE TABLE IF NOT EXISTS Version ( id INTEGER PRIMARY KEY AUTOINCREMENT, version text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ); INSERT INTO Version(version) values('1.0'); } 2 { CREATE TABLE IF NOT EXISTS Tags ( id INTEGER PRIMARY KEY AUTOINCREMENT, name text, tag text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ); ALTER TABLE Notes ADD COLUMN dump text; INSERT INTO Version(version) values('2.0'); } 3 { ALTER TABLE Version ADD COLUMN sql text; INSERT INTO Version(version) values('3.0'); } } # create db command , use in memory database for demonstration purposes sqlite3 db :memory: proc createSchema { sqlArray } { upvar $sqlArray sql # execute each sql script in order foreach version [lsort -integer [array names sql ] ] { set cmd $sql($version) set ok 0 while { !$ok && [string length $cmd ] } { try { db eval $cmd set ok 1 ; # it succeeded if we get here } on error { err backtrace } { if { [regexp {duplicate column name: ([a-zA-Z0-9])} [string trim $err ] match columnname ] } { puts "Error: $err ... trying again" set cmd [removeAlterTable $cmd $columnname ] } else { throw DBERROR "$err\n$backtrace" } } } } } # return sqltext with alter table command with column name removed # if no matching alter table line found or result is no lines then # returns "" proc removeAlterTable { sqltext columnname } { set mode skip set result [list] foreach line [split $sqltext \n ] { if { [string first "alter table" [string tolower [string trim $line] ] ] >= 0 } { if { [string first $columnname $line ] } { set mode add continue; } } if { $mode eq "add" } { lappend result $line } } if { $mode eq "skip" } { puts stderr "Unable to find matching alter table line" return "" } elseif { [llength $result ] } { return [ join $result \n ] } else { return "" } } proc printSchema { } { db eval { select * from sqlite_master } x { puts "Table: $x(tbl_name)" puts "$x(sql)" puts "-------------" } } createSchema sqlArray printSchema # run again to see if we get alter table errors createSchema sqlArray printSchema
keluaran yang diharapkan
Table: Notes CREATE TABLE Notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, name text, note text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , dump text) ------------- Table: sqlite_sequence CREATE TABLE sqlite_sequence(name,seq) ------------- Table: Version CREATE TABLE Version ( id INTEGER PRIMARY KEY AUTOINCREMENT, version text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , sql text) ------------- Table: Tags CREATE TABLE Tags ( id INTEGER PRIMARY KEY AUTOINCREMENT, name text, tag text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ) ------------- Error: duplicate column name: dump ... trying again Error: duplicate column name: sql ... trying again Table: Notes CREATE TABLE Notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, name text, note text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , dump text) ------------- Table: sqlite_sequence CREATE TABLE sqlite_sequence(name,seq) ------------- Table: Version CREATE TABLE Version ( id INTEGER PRIMARY KEY AUTOINCREMENT, version text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , sql text) ------------- Table: Tags CREATE TABLE Tags ( id INTEGER PRIMARY KEY AUTOINCREMENT, name text, tag text, createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) , updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ) -------------
sumber
select * from sqlite_master where type = 'table' and tbl_name = 'TableName' and sql like '%ColumnName%'
Logika: kolom sql di sqlite_master berisi definisi tabel, jadi pasti berisi string dengan nama kolom.
Saat Anda mencari sub-string, ia memiliki batasan yang jelas. Jadi saya akan menyarankan untuk menggunakan sub-string yang lebih ketat di ColumnName, misalnya sesuatu seperti ini (tunduk pada pengujian karena karakter '"' tidak selalu ada):
select * from sqlite_master where type = 'table' and tbl_name = 'MyTable' and sql like '%`MyColumn` TEXT%'
sumber
Saya menyelesaikannya dalam 2 pertanyaan. Ini adalah skrip Unity3D saya menggunakan System.Data.SQLite.
IDbCommand command = dbConnection.CreateCommand(); command.CommandText = @"SELECT count(*) FROM pragma_table_info('Candidat') c WHERE c.name = 'BirthPlace'"; IDataReader reader = command.ExecuteReader(); while (reader.Read()) { try { if (int.TryParse(reader[0].ToString(), out int result)) { if (result == 0) { command = dbConnection.CreateCommand(); command.CommandText = @"ALTER TABLE Candidat ADD COLUMN BirthPlace VARCHAR"; command.ExecuteNonQuery(); command.Dispose(); } } } catch { throw; } }
sumber