ALTER TABLE ADD COLUMN ЕСЛИ НЕ СУЩЕСТВУЕТ в SQLite - PullRequest
74 голосов
/ 31 августа 2010

Недавно у нас возникла необходимость добавить столбцы в несколько существующих таблиц базы данных SQLite.Это можно сделать с помощью ALTER TABLE ADD COLUMN.Конечно, если таблица уже была изменена, мы хотим оставить ее в покое.К сожалению, SQLite не поддерживает предложение IF NOT EXISTS для ALTER TABLE.

. Наш текущий обходной путь - выполнить инструкцию ALTER TABLE и игнорировать любые ошибки «повторяющегося имени столбца», как и этого Python.пример (но в C ++).

Тем не менее, наш обычный подход к настройке схем базы данных состоит в том, чтобы иметь скрипт .sql, содержащий операторы CREATE TABLE IF NOT EXISTS и CREATE INDEX IF NOT EXISTS, которые могут быть выполнены с использованием sqlite3_exec или sqlite3 инструмент командной строки.Мы не можем поместить ALTER TABLE в эти файлы сценариев, потому что если этот оператор завершится неудачно, что-нибудь после того, как оно не будет выполнено.

Я хочу, чтобы определения таблиц находились в одном месте, а не разбивались на .sql и.cpp файлы.Есть ли способ написать обходной путь к ALTER TABLE ADD COLUMN IF NOT EXISTS в чистом SQLite SQL?

Ответы [ 7 ]

50 голосов
/ 31 августа 2010

У меня 99% метод чистого SQL.Идея состоит в том, чтобы создать версию вашей схемы.Это можно сделать двумя способами:

  • Используйте команду прагмы 'user_version' (PRAGMA user_version), чтобы сохранить инкрементный номер для версии схемы базы данных.

  • Сохраните номер своей версии в своей собственной определенной таблице.

Таким образом, при запуске программного обеспечения оно может проверить схему базы данных и, если необходимо, выполните запрос ALTER TABLE, затем увеличьте сохраненную версию.Это намного лучше, чем пытаться делать всевозможные «слепые» обновления, особенно если ваша база данных растет и изменяется несколько раз за эти годы.

28 голосов
/ 23 апреля 2015

Один из обходных путей - просто создать столбцы и перехватить исключение / ошибку, которые возникают, если столбец уже существует.При добавлении нескольких столбцов добавляйте их в отдельные операторы 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));
    }
}
25 голосов
/ 28 сентября 2012

SQLite также поддерживает прагматический оператор "table_info", который возвращает одну строку на столбец в таблице с именем столбца (и другой информацией о столбце). Вы можете использовать это в запросе для проверки отсутствующего столбца и, если его нет, изменить таблицу.

PRAGMA table_info(foo_table_name)

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

17 голосов
/ 20 апреля 2017

Если вы делаете это в операторе обновления БД, возможно, самый простой способ - просто перехватить выброшенное исключение, если вы пытаетесь добавить поле, которое может уже существовать.

try {
   db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN foo TEXT default null");
} catch (SQLiteException ex) {
   Log.w(TAG, "Altering " + TABLE_NAME + ": " + ex.getMessage());
}
11 голосов
/ 01 декабря 2016

есть метод 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);
0 голосов
/ 21 июня 2019

Я взял ответ выше в C # /. Net и переписал его для Qt / C ++, не сильно изменив его, но я хотел оставить его здесь для любого, кто в будущем ищет ответ C ++ 'ish'.

    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 голосов
/ 24 июля 2014

Если у вас возникла эта проблема в flex / adobe air и вы оказались здесь первыми, я нашел решение и разместил его по связанному вопросу: ДОБАВЬТЕ КОЛОННУ в sqlite db, ЕСЛИ НЕ СУЩЕСТВУЕТ - flex / air sqlite?

Мой комментарий здесь: https://stackoverflow.com/a/24928437/2678219

...