Comment vérifier dans SQLite si une table existe?

895

Comment puis-je vérifier de manière fiable dans SQLite si une table utilisateur particulière existe?

Je ne demande pas de moyens peu fiables comme vérifier si un "select *" sur la table a retourné une erreur ou non (est-ce même une bonne idée?).

La raison est la suivante:

Dans mon programme, je dois créer puis remplir certaines tables si elles n'existent pas déjà.

S'ils existent déjà, j'ai besoin de mettre à jour certaines tables.

Dois-je prendre un autre chemin à la place pour signaler que les tables en question ont déjà été créées - par exemple, en créant / plaçant / définissant un certain indicateur dans mon fichier d'initialisation / paramètres de programme sur le disque ou quelque chose?

Ou mon approche est-elle logique?

PoorLuzer
la source
SQLite lèvera une exception si la table dans une sélection n'existe pas. Il n'y a tout simplement pas besoin de travaux plus sophistiqués.
NoChance
34
@NoChance, mais il en sera de même pour un certain nombre d'autres choses. C'est un peu comme voir si cet arbre est vraiment là en avançant les yeux fermés, vous découvrirez d'une façon ou d'une autre :)
randomsock
@randomsock, bel exemple, mais un peu effrayant, surtout si la voiture était ma voiture ...
NoChance
@randomsock, je ne sais pas quelle est la convention sqlite, mais c'est plus pythonique de demander pardon que permission. c'est-à-dire attraper l'exception au lieu d'utiliser un conditionnel.
Eric
1
@Eric Pour l'instant, la question n'implique pas Python, mais en supposant que ce soit le cas, l'erreur est générique sqlite3.OperationalError, vous devez donc analyser le message d'erreur afin de vous assurer que c'est par exemple le message "table TABLE_NAME existe déjà" lorsque vous créez une table, et sinon, relancez l'erreur et je pense qu'il n'y a aucune garantie que la formulation de l'erreur ne changera pas.
Markus von Broady

Réponses:

1023

J'ai raté cette entrée FAQ.

Quoi qu'il en soit, pour référence future, la requête complète est:

SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';

{table_name}est le nom de la table à vérifier.

Section de documentation pour référence: Format de fichier de base de données. 2.6. Stockage du schéma de base de données SQL

  • Cela renverra une liste de tables avec le nom spécifié; c'est-à-dire que le curseur aura un compte de 0 (n'existe pas) ou un compte de 1 (existe)
PoorLuzer
la source
7
Laquelle de la documentation SQLite couvre ces tables système?
Pawel Veselov
29
@Pawel Veselov: La section intitulée "Format de fichier pour les bases de données SQLite": sqlite.org/fileformat2.html
Bryan Oakley
14
Cependant, cela ne fonctionnera pas pour les tables TEMP. Les tables TEMP sont dans "sqlite_temp_master".
PatchyFog
11
Est-ce que cela renvoie un booléen? Que retourne-t-il si la table existe ou n'existe pas?
Dagrooms
8
@Dagrooms Cela retournera une liste de tables avec le nom spécifié; c'est-à-dire que le curseur aura un compte de 0 (n'existe pas) ou un compte de 1 (existe).
Rein S
555

Si vous utilisez SQLite version 3.3+, vous pouvez facilement créer une table avec:

create table if not exists TableName (col1 typ1, ..., colN typN)

De la même manière, vous ne pouvez supprimer une table que si elle existe en utilisant:

drop table if exists TableName
Arthur johnston
la source
3
Notez que l' create tableinstruction est incomplète (il manque la spécification des colonnes de table).
Eric Platon
11
il existe également une construction similaire pour les index: créer un index s'il n'existe pas TableName_col1 sur TableName (col1)
lowtech
26
Cela ne devrait pas être la réponse acceptée, mais le serait si la question était formulée différemment. L'OP n'a pas demandé comment vérifier une table avant de la supprimer ou de la créer. Et si vous devez interroger une table qui n'existe peut-être pas? C'est le problème auquel je suis confronté maintenant, et la réponse acceptée fonctionne mieux dans cette déclaration de problème générale. C'est une bonne alternative rapide.
Dagrooms
@Dagrooms, vous avez peut-être raison. Bien que l'OP ne l'ait pas demandé, je cherchais cette réponse :)
earik87
169

Une variante consisterait à utiliser SELECT COUNT (*) au lieu de SELECT NAME, c'est-à-dire

SELECT count(*) FROM sqlite_master WHERE type='table' AND name='table_name';

Cela retournera 0, si la table n'existe pas, 1 si elle existe. Ceci est probablement utile dans votre programmation car un résultat numérique est plus rapide / plus facile à traiter. Ce qui suit illustre comment vous le feriez dans Android en utilisant SQLiteDatabase, Cursor, rawQuery avec des paramètres.

boolean tableExists(SQLiteDatabase db, String tableName)
{
    if (tableName == null || db == null || !db.isOpen())
    {
        return false;
    }
    Cursor cursor = db.rawQuery("SELECT COUNT(*) FROM sqlite_master WHERE type = ? AND name = ?", new String[] {"table", tableName});
    if (!cursor.moveToFirst())
    {
        cursor.close();
        return false;
    }
    int count = cursor.getInt(0);
    cursor.close();
    return count > 0;
}
Stephen Quan
la source
33
Je pense qu'un "SELECT 1" serait encore plus rapide.
PatchyFog
Pourquoi le curseur.getInt (0) est égal au nombre d'enregistrements dans la base de données?
Semyon Danilov
1
Nous comptons le nombre de fois que la TABLE apparaît dans le schéma sqlite. Un nombre de 0 signifie que la table n'existe pas. Un nombre de 1 signifie que la table existe. Ce sont les deux seules valeurs de comptage attendues.
Stephen Quan
1
Bien que le nombre (de COUNT(*)) soit facile à traiter, il est encore plus facile de renvoyer l'existence d'une ligne ou non; s'il y a une ligne, alors elle existe, s'il n'y a pas de ligne, elle n'existe pas. (Vous vérifiez déjà l'échec de moveToFirst, donc le travail serait fait à ce moment-là.)
dash-tom-bang
Veuillez mettre à jour votre code pour fermer le curseur avant de retourner false.
Dave Thomas
43

Tu pourrais essayer:

SELECT name FROM sqlite_master WHERE name='table_name'
Galvanais
la source
4
type = table serait utile tho
mafu
Si vous utilisez C #, n'utilisez pas cette commande dans a SQLiteReader reader = cmd.ExecuteReader();et faites un dt.Load(reader)(où dtest a DataTable). J'ai trouvé qu'il donne cette Object reference is not an instance of an objectexception sur le .Load()si la table n'est pas trouvée. Utilisez plutôt a SQLiteDataAdapter adapter = new SQLiteDataAdapter(cmd); et do adapter.Fill(ds), où dsest a DataSet. Vous pouvez alors voir si ds.Tables.Count > 0et return ds.Tables[0];si oui (ou else return null). Ensuite, vous pouvez vérifier cela DataTablepour être null, si dt.Rows != nullet sidt.Rows.Count>0
vapcguy
35

Utilisation:

PRAGMA table_info(your_table_name)

Si la table résultante est vide, elle your_table_namen'existe pas.

Documentation:

PRAGMA schema.table_info (nom-table);

Ce pragma renvoie une ligne pour chaque colonne de la table nommée. Les colonnes de l'ensemble de résultats incluent le nom de la colonne, le type de données, si la colonne peut être NULL et la valeur par défaut de la colonne. La colonne "pk" dans le jeu de résultats est nulle pour les colonnes qui ne font pas partie de la clé primaire et est l'index de la colonne dans la clé primaire pour les colonnes qui font partie de la clé primaire.

La table nommée dans le pragma table_info peut également être une vue.

Exemple de sortie:

cid|name|type|notnull|dflt_value|pk
0|id|INTEGER|0||1
1|json|JSON|0||0
2|name|TEXT|0||0
Diego Vélez
la source
C'est un excellent moyen de déterminer si une table existe en Python.
Michael Murphy
ou Xamarin Forms
SerenityNow
4
C'est un excellent moyen d'accéder aux définitions de colonne par programme
w00t
33

Les noms de table SQLite ne respectent pas la casse, mais la comparaison est sensible à la casse par défaut. Pour que cela fonctionne correctement dans tous les cas, vous devez ajouter COLLATE NOCASE.

SELECT name FROM sqlite_master WHERE type='table' AND name='table_name' COLLATE NOCASE
Brice M. Dempsey
la source
33

Si vous obtenez une erreur "la table existe déjà", apportez les modifications dans la chaîne SQL comme ci-dessous:

CREATE table IF NOT EXISTS table_name (para1,para2);

De cette façon, vous pouvez éviter les exceptions.

Rakesh Chaudhari
la source
32

Voir ceci :

SELECT name FROM sqlite_master
WHERE type='table'
ORDER BY name;
Anton Gogolev
la source
23

Si vous utilisez fmdb , je pense que vous pouvez simplement importer FMDatabaseAdditions et utiliser la fonction bool:

[yourfmdbDatabase tableExists:tableName].
user655489
la source
1
Assurez-vous d'importer "FMDatabaseAdditions.h" afin d'utiliser cette méthode, sinon vous vous demanderez pourquoi ils l'ont supprimé! :)
Will
Bien que cela puisse être une bonne réponse, la question concernait sqlite et non une bibliothèque particulière dans une langue particulière. Je pense que la réponse devrait être de fournir du code sql, pas un appel à l'une des méthodes de la bibliothèque
nacho4d
13

Le code suivant renvoie 1 si la table existe ou 0 si la table n'existe pas.

SELECT CASE WHEN tbl_name = "name" THEN 1 ELSE 0 END FROM sqlite_master WHERE tbl_name = "name" AND type = "table"
pacheco
la source
1
Cela ne retournera toujours rien si la table n'existe pas, car la condition where empêche tout résultat.
David Gausmann
10

Notez que pour vérifier si une table existe dans la base de données TEMP, vous devez utiliser à la sqlite_temp_masterplace de sqlite_master:

SELECT name FROM sqlite_temp_master WHERE type='table' AND name='table_name';
Scott Deerwester
la source
9

Voici la fonction que j'ai utilisée:

Étant donné un objet SQLDatabase = db

public boolean exists(String table) {
    try {
         db.query("SELECT * FROM " + table);
         return true;
    } catch (SQLException e) {
         return false;
    }
}
DroidGrailer
la source
1
J'ai malheureusement dû utiliser cela dans mon application Android, car j'ai constaté que les appareils Samsung n'utilisaient pas la structure de table sqlite_master standard avec laquelle tout le monde travaille.
Anthony Chuinard
7

Utilisez ce code:

SELECT name FROM sqlite_master WHERE type='table' AND name='yourTableName';

Si le nombre de tableaux retournés est égal à 1, cela signifie que la table existe. Sinon, il n'existe pas.

asmad
la source
4
class CPhoenixDatabase():
    def __init__(self, dbname):
        self.dbname = dbname
        self.conn = sqlite3.connect(dbname)

    def is_table(self, table_name):
        """ This method seems to be working now"""
        query = "SELECT name from sqlite_master WHERE type='table' AND name='{" + table_name + "}';"
        cursor = self.conn.execute(query)
        result = cursor.fetchone()
        if result == None:
            return False
        else:
            return True

Remarque: cela fonctionne maintenant sur mon Mac avec Python 3.7.1

Douglas Goodall
la source
Cela semble plus propre que toutes les autres réponses .. Merci !!
Harsha Vardhan
Ça ne marche pas pour moi: je dois effacer les crochets {} autour de nom_table, puis ça va.
Banana
1
Assurez-vous qu'il table_namen'est pas fourni par une source non utilisée (comme les entrées utilisateur), sinon il sera vulnérable à l'injection SQL. Il est toujours préférable d'utiliser des paramètres plutôt que des techniques de manipulation de texte
astef
3

Utilisation

SELECT 1 FROM table LIMIT 1;

pour empêcher la lecture de tous les enregistrements.

Franz Fahrenkrog Petermann
la source
Cela renvoie NULL si la table existe mais n'a aucun enregistrement.
radiospiel
Si la table n'existe pas, elle générera une erreur. Attrapez cela, et vous savez qu'il n'existe pas.
luckydonald
l'utilisation de la gestion des erreurs comme contrôle de flux n'est généralement pas considérée comme la meilleure pratique. Cela devrait probablement être évité.
Jeff Woodard
3

Vous pouvez écrire la requête suivante pour vérifier l'existence de la table.

SELECT name FROM sqlite_master WHERE name='table_name'

Ici, 'table_name' est le nom de votre table que vous avez créé. Par exemple

 CREATE TABLE IF NOT EXISTS country(country_id INTEGER PRIMARY KEY AUTOINCREMENT, country_code TEXT, country_name TEXT)"

et vérifie

  SELECT name FROM sqlite_master WHERE name='country'
akn
la source
6
En quoi est-ce différent de la réponse la plus votée d'il y a 9 ans déjà acceptée?
Kevin Van Dyck
3

Le moyen le plus fiable que j'ai trouvé en C # en ce moment, en utilisant le dernier paquet nuget sqlite-net-pcl (1.5.231) qui utilise SQLite 3, est le suivant:

var result = database.GetTableInfo(tableName);
if ((result == null) || (result.Count == 0))
{
    database.CreateTable<T>(CreateFlags.AllImplicit);
}
Matthew Joughin
la source
2

L'utilisation d'une simple requête SELECT est - à mon avis - assez fiable. Surtout, il peut vérifier l'existence de la table dans de nombreux types de bases de données différents (SQLite / MySQL).

SELECT 1 FROM table;

Il est logique lorsque vous pouvez utiliser un autre mécanisme fiable pour déterminer si la requête a réussi (par exemple, vous interrogez une base de données via QSqlQuery dans Qt ).

Grz
la source
1

La fonction c ++ vérifie la base de données et toutes les bases de données attachées pour l'existence de la table et (éventuellement) de la colonne.

bool exists(sqlite3 *db, string tbl, string col="1")
{
    sqlite3_stmt *stmt;
    bool b = sqlite3_prepare_v2(db, ("select "+col+" from "+tbl).c_str(),
    -1, &stmt, 0) == SQLITE_OK;
    sqlite3_finalize(stmt);
    return b;
}

Edit: récemment découvert la fonction sqlite3_table_column_metadata. Par conséquent

bool exists(sqlite3* db,const char *tbl,const char *col=0)
{return sqlite3_table_column_metadata(db,0,tbl,col,0,0,0,0,0)==SQLITE_OK;}
NoComprende
la source
public static boolean tableExists (base de données SQLiteDatabase, String tableName) {return database.rawQuery ("SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tableName + "'", null) .moveToFirst (); }
nick
Manière très inefficace et risquée car la concaténation de chaînes peut finir à tout.
Andrea Moro
0

Voici mon code pour SQLite Cordova:

get_columnNames('LastUpdate', function (data) {
    if (data.length > 0) { // In data you also have columnNames
        console.log("Table full");
    }
    else {
        console.log("Table empty");
    }
});

Et l'autre:

function get_columnNames(tableName, callback) {
    myDb.transaction(function (transaction) {
        var query_exec = "SELECT name, sql FROM sqlite_master WHERE type='table' AND name ='" + tableName + "'";
        transaction.executeSql(query_exec, [], function (tx, results) {
            var columnNames = [];
            var len = results.rows.length;
            if (len>0){
                var columnParts = results.rows.item(0).sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').split(','); ///// RegEx
                for (i in columnParts) {
                    if (typeof columnParts[i] === 'string')
                        columnNames.push(columnParts[i].split(" ")[0]);
                };
                callback(columnNames);
            }
            else callback(columnNames);
        });
    });
}
Zappescu
la source
0

Je pensais que je mettrais mes 2 cents à cette discussion, même si elle est plutôt ancienne. Cette requête renvoie scalaire 1 si la table existe et 0 sinon.

select 
    case when exists 
        (select 1 from sqlite_master WHERE type='table' and name = 'your_table') 
        then 1 
        else 0 
    end as TableExists
Piotr Rodak
la source
0

La table existe ou pas dans la base de données dans swift

func tableExists(_ tableName:String) -> Bool {
        sqlStatement = "SELECT name FROM sqlite_master WHERE type='table' AND name='\(tableName)'"
        if sqlite3_prepare_v2(database, sqlStatement,-1, &compiledStatement, nil) == SQLITE_OK {
            if sqlite3_step(compiledStatement) == SQLITE_ROW {
                return true
            }
            else {
                return false
            }
        }
        else {
            return false
        }
            sqlite3_finalize(compiledStatement)
    }
CSE 1994
la source