Nous avons récemment eu besoin d'ajouter des colonnes à quelques-unes de nos tables de base de données SQLite existantes. Cela peut être fait avec ALTER TABLE ADD COLUMN
. Bien sûr, si la table a déjà été modifiée, nous voulons la laisser seule. Malheureusement, SQLite ne supporte pas un IF NOT EXISTS
clause sur ALTER TABLE
.
Notre solution actuelle consiste à exécuter l'instruction ALTER TABLE et à ignorer les erreurs de type "nom de colonne en double", comme this Python (mais en C++).
Cependant, notre approche habituelle pour configurer des schémas de base de données consiste à utiliser un script .sql contenant CREATE TABLE IF NOT EXISTS
et CREATE INDEX IF NOT EXISTS
instructions, qui peuvent être exécutées avec sqlite3_exec
ou la sqlite3
outil en ligne de commande. Nous ne pouvons pas mettre ALTER TABLE
dans ces fichiers de script, car si cette instruction échoue, toute action ultérieure ne sera pas exécutée.
Je veux avoir les définitions de table dans un endroit et pas divisé entre les fichiers .sql et .cpp. Est-il possible d'écrire une solution de contournement à ALTER TABLE ADD COLUMN IF NOT EXISTS
en SQLite SQL pur?
J'ai une méthode de SQL pur à 99%. L'idée est de mettre à jour votre schéma. Vous pouvez le faire de deux manières:
Utilisez la commande pragma 'user_version' ( PRAGMA user_version
) pour stocker un numéro incrémentiel pour la version de votre schéma de base de données.
Stockez votre numéro de version dans votre propre table définie.
De cette manière, lorsque le logiciel est démarré, il peut vérifier le schéma de la base de données et, si nécessaire, exécuter votre ALTER TABLE
requête, puis incrémenter la version stockée. C'est de loin préférable à diverses tentatives de mise à jour "à l'aveugle", surtout si votre base de données s'agrandit et change plusieurs fois au fil des ans.
Une solution de contournement consiste à simplement créer les colonnes et à récupérer les exceptions/erreurs qui se produisent si la colonne existe déjà. Lors de l'ajout de plusieurs colonnes, ajoutez-les dans des instructions ALTER TABLE séparées afin qu'un duplicata n'empêche pas la création des autres.
Avec sqlite-net , nous avons fait quelque chose comme ça. Ce n'est pas parfait, car nous ne pouvons pas distinguer les erreurs SQLite en double des autres erreurs 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));
}
}
SQLite prend également en charge une instruction pragma appelée "table_info" qui renvoie une ligne par colonne dans une table avec le nom de la colonne (ainsi que d'autres informations sur la colonne). Vous pouvez l'utiliser dans une requête pour rechercher la colonne manquante. Sinon, modifiez la table.
PRAGMA table_info(foo_table_name)
Si vous faites cela dans une instruction de mise à niveau de base de données, le moyen le plus simple consiste peut-être simplement à attraper l'exception levée si vous essayez d'ajouter un champ qui existe peut-être déjà.
try {
db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN foo TEXT default null");
} catch (SQLiteException ex) {
Log.w(TAG, "Altering " + TABLE_NAME + ": " + ex.getMessage());
}
voici une méthode de PRAGMA appelée table_info (nom_table), elle renvoie toutes les informations de la table.
Voici l'implémentation, comment l'utiliser pour vérifier que la colonne existe ou non,
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;
}
Vous pouvez également utiliser cette requête sans utiliser de boucle,
cursor = db.rawQuery("PRAGMA table_info("+ table +") where name = " + column, null);
J'ai pris la réponse ci-dessus dans C # /. Net et je l'ai réécrite pour Qt/C++, cela n'a pas trop changé, mais je voulais la laisser ici pour quiconque à la recherche d'une réponse 'ish' en 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;
}
Si vous rencontrez ce problème dans flex/Adobe air et que vous vous trouvez d'abord ici, j'ai trouvé une solution et je l'ai postée sur une question connexe: AJOUTER COLONNE à sqlite db SI EXISTE - flex/air sqlite?
Mon commentaire ici: https://stackoverflow.com/a/24928437/2678219