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?


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)

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

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

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'

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

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

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.



23

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

[yourfmdbDatabase tableExists:tableName].

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"

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

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

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.


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


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.


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'

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

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 ).


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

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

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

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)
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.