web-dev-qa-db-ja.com

SQLiteに存在しない場合はALTER TABLE ADD COLUMN

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

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

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

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

75
dan04

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

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

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

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

50
MPelletier

回避策の1つは、列を作成し、列が既に存在する場合に発生する例外/エラーをキャッチすることです。複数の列を追加する場合、1つの重複が他の列の作成を妨げないように、別々のALTER TABLEステートメントに追加します。

sqlite-net で、このようなことをしました。重複するsqliteエラーを他のsqliteエラーと区別できないため、完全ではありません。

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

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

PRAGMA table_info(foo_table_name)

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

25
Robert Hawkey

DBアップグレードステートメントでこれを行う場合、おそらく最も簡単な方法は、既に存在する可能性のあるフィールドを追加しようとする場合にスローされる例外をキャッチすることです。

try {
   db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN foo TEXT default null");
} catch (SQLiteException ex) {
   Log.w(TAG, "Altering " + TABLE_NAME + ": " + ex.getMessage());
}
19
user7896780

threreはPRAGMAのメソッドであるtable_info(table_name)であり、tableのすべての情報を返します。

これは、列が存在するかどうかを確認するために使用する実装です。

    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);
11
Krunal Shah

上記の答えをC#/。Netで取得し、Qt/C++に書き直しましたが、あまり変更されていませんが、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;
}
0
Kevin B Burns

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

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

0
stevesweets