99

最近、いくつかの既存の SQLite データベース テーブルに列を追加する必要がありました。これは で行うことができますALTER TABLE ADD COLUMN。もちろん、テーブルが既に変更されている場合はそのままにしておきます。IF NOT EXISTS残念ながら、SQLite は の句をサポートしていませんALTER TABLE

現在の回避策は、ALTER TABLE ステートメントを実行し、この Python の例のように (ただし C++ で)、「重複する列名」エラーを無視することです。

ただし、データベース スキーマを設定するための通常のアプローチは、またはコマンド ライン ツールを使用して実行できるCREATE TABLE IF NOT EXISTSandステートメントを含む .sql スクリプトを用意することです。これらのスクリプト ファイルを挿入することはできません。そのステートメントが失敗すると、それ以降は実行されないからです。CREATE INDEX IF NOT EXISTSsqlite3_execsqlite3ALTER TABLE

テーブル定義を .sql ファイルと .cpp ファイルに分割せずに 1 か所にまとめたいと考えています。ALTER TABLE ADD COLUMN IF NOT EXISTS純粋な SQLite SQL で回避策を書く方法はありますか?

4

15 に答える 15

66

私は99%純粋なSQLメソッドを持っています。アイデアは、スキーマをバージョン管理することです。これは2つの方法で実行できます。

  • 'user_version'プラグマコマンド(PRAGMA user_version)を使用して、データベーススキーマバージョンの増分番号を格納します。

  • 独自に定義したテーブルにバージョン番号を保存します。

このようにして、ソフトウェアの起動時に、データベーススキーマを確認し、必要に応じてALTER TABLEクエリを実行して、保存されているバージョンをインクリメントできます。これは、さまざまな更新を「ブラインド」で試行するよりもはるかに優れています。特に、データベースが何年にもわたって数回大きくなり、変更される場合はなおさらです。

于 2010-08-30T21:35:57.750 に答える
30

SQLite は、「table_info」と呼ばれるプラグマ ステートメントもサポートしています。これは、列の名前 (および列に関するその他の情報) を含むテーブルの列ごとに 1 つの行を返します。これをクエリで使用して、欠落している列を確認し、存在しない場合はテーブルを変更できます。

PRAGMA table_info(foo_table_name)

出力例:

シド 名前 タイプ ヌルでない dflt_value パック
0 ID 整数 0 ヌル 1
1 タイプ 文章 0 ヌル 0
2 データ json 0 ヌル 0

http://www.sqlite.org/pragma.html#pragma_table_info

于 2012-09-28T12:11:15.967 に答える
13

PRAGMA のメソッドは table_info(table_name) で、テーブルのすべての情報を返します。

これは、チェック列が存在するかどうかに使用する方法の実装です。

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

ループを使用せずにこのクエリを使用することもできます。

cursor = db.rawQuery("PRAGMA table_info("+ table +") where name = " + column, null);
于 2016-12-01T10:58:26.490 に答える
0

flex/adobe air でこの問題が発生し、最初にここにいる場合は、解決策を見つけて、関連する質問に投稿しました: ADD COLUMN to sqlite db IF NOT EXISTS - flex/air sqlite?

ここでの私のコメント: https://stackoverflow.com/a/24928437/2678219

于 2014-07-24T08:39:26.177 に答える
0
select * from sqlite_master where type = 'table' and tbl_name = 'TableName' and sql like '%ColumnName%'

ロジック: sqlite_master の sql 列にはテーブル定義が含まれているため、列名の文字列が含まれていることは確かです。

部分文字列を検索しているため、明らかな制限があります。したがって、ColumnName でさらに制限的な部分文字列を使用することをお勧めします。たとえば、次のようなものです (「`」文字が常に存在するとは限らないため、テストの対象となります)。

select * from sqlite_master where type = 'table' and tbl_name = 'MyTable' and sql like '%`MyColumn` TEXT%'
于 2020-11-03T09:12:13.560 に答える
0

一度に 1 行ずつ実行すれば、これらすべての答えは問題ありません。ただし、元の質問は、単一の db 実行によって実行される sql スクリプトを入力することであり、すべてのソリューション (列が事前に存在するかどうかを確認するなど) では、実行プログラムがどのテーブルと列が変更/追加されているか、入力スクリプトの前処理と解析を行って、この情報を判別します。通常、これをリアルタイムで、または頻繁に実行することはありません。したがって、例外をキャッチして次に進むという考えは受け入れられます。そこに問題があります...どうやって先に進むか。幸いなことに、エラー メッセージから、これを行うために必要なすべての情報が得られます。アイデアは、alter table 呼び出しで例外が発生した場合に sql を実行し、sql で alter table 行を見つけて残りの行を返し、成功するか、一致する alter table 行が見つからなくなるまで実行することです。配列に sql スクリプトがあるサンプル コードを次に示します。各スクリプトを実行する配列を反復処理します。これを 2 回呼び出して、alter table コマンドを失敗させますが、SQL から alter table コマンドを削除して更新されたコードを再実行するため、プログラムは成功します。

#!/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

期待される出力

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 ) ) 
        )
-------------
于 2020-08-16T01:19:49.260 に答える