Quelle est la meilleure façon de vérifier si une table existe dans une base de données SQL de manière indépendante de la base de données?
Je suis venu avec:
bool exists;
const string sqlStatement = @"SELECT COUNT(*) FROM my_table";
try
{
using (OdbcCommand cmd = new OdbcCommand(sqlStatement, myOdbcConnection))
{
cmd.ExecuteScalar();
exists = true;
}
}
catch
{
exists = false;
}
Y a-t-il une meilleure manière de faire cela? Cette méthode ne fonctionnera pas lorsque la connexion à la base de données échoue. J'ai trouvé des moyens pour Sybase, SQL Server, Oracle mais rien qui fonctionne pour toutes les bases de données.
bool exists;
try
{
// ANSI SQL way. Works in PostgreSQL, MSSQL, MySQL.
var cmd = new OdbcCommand(
"select case when exists((select * from information_schema.tables where table_name = '" + tableName + "')) then 1 else 0 end");
exists = (int)cmd.ExecuteScalar() == 1;
}
catch
{
try
{
// Other RDBMS. Graceful degradation
exists = true;
var cmdOthers = new OdbcCommand("select 1 from " + tableName + " where 1 = 0");
cmdOthers.ExecuteNonQuery();
}
catch
{
exists = false;
}
}
Je ne pense pas qu'il existe une façon générique qui fonctionne pour toutes les bases de données, car c'est quelque chose de très spécifique qui dépend de la façon dont la base de données est construite.
Mais, pourquoi voulez-vous faire cela en utilisant une requête spécifique? Ne pouvez-vous pas retirer l'implémentation de ce que vous voulez faire? Je veux dire: pourquoi ne pas créer une interface générique, qui a entre autres, une méthode appelée 'TableExists (string tablename)' par exemple. Ensuite, pour chaque SGBD que vous souhaitez prendre en charge, vous créez une classe qui implémente cette interface, et dans la méthode TableExists, vous écrivez une logique spécifique pour ce SGBD.
L'implémentation SQLServer contiendra alors une requête qui interroge les sysobjects.
Dans votre application, vous pouvez avoir une classe d'usine qui crée l'implémentation correcte pour un contexte donné, puis vous appelez simplement la méthode TableExists.
Par exemple:
IMyInterface foo = MyFactory.CreateMyInterface (SupportedDbms.SqlServer);
if( foo.TableExists ("mytable") )
...
Je pense que c'est ainsi que je dois le faire.
Si vous essayez l'indépendance de la base de données, vous devrez assumer une norme minimale. IIRC Les vues ANSI INFORMATION_SCHEMA sont requises pour la conformité ODBC, vous pouvez donc les interroger comme:
select count (*)
from information_schema.tables
where table_name = 'foobar'
Étant donné que vous utilisez ODBC, vous pouvez également utiliser divers appels API ODBC pour récupérer également ces métadonnées.
Gardez à l'esprit que la portabilité équivaut à test en écriture unique n'importe où vous devrez donc toujours tester l'application sur chaque plate-forme que vous avez l'intention de prendre en charge. Cela signifie que vous êtes intrinsèquement limité à un nombre fini de plates-formes de base de données possibles car vous ne disposez que de beaucoup de ressources pour les tests.
Le résultat est que vous devez trouver le plus petit dénominateur commun pour votre application (ce qui est beaucoup plus difficile qu'il n'y paraît pour SQL) ou créer une section dépendante de la plate-forme où les fonctions non portables peuvent être connectées sur une plate-forme par base.
J'éviterais d'exécuter la select count(x) from xxxxxx
car le SGBD ira de l'avant et le fera, ce qui peut prendre un certain temps pour une grande table.
Au lieu de cela, préparer une select * from mysterytable
requete. La préparation échouera si la table mystère n'existe pas. Il n'est pas nécessaire d'exécuter réellement l'instruction préparée.
Je soutiens pleinement la réponse de Frederik Gheysels. Si vous devez prendre en charge plusieurs systèmes de base de données, vous devez implémenter votre code par rapport à une interface abstraite avec des implémentations spécifiques par système de base de données. Il existe de nombreux autres exemples de syntaxe incompatible que la simple vérification d'une table existante (par exemple: limiter la requête à un certain nombre de lignes).
Mais si vous devez vraiment effectuer la vérification en utilisant la gestion des exceptions de votre exemple, vous devez utiliser la requête suivante qui est plus efficace qu'un COUNT (*) car la base de données n'a aucun travail de sélection réel à faire:
SELECT 1 FROM my_table WHERE 1=2
Ce qui suit fonctionne bien pour moi ...
private bool TableExists(SqlConnection conn, string database, string name)
{
string strCmd = null;
SqlCommand sqlCmd = null;
try
{
strCmd = "select case when exists((select '['+SCHEMA_NAME(schema_id)+'].['+name+']' As name FROM [" + database + "].sys.tables WHERE name = '" + name + "')) then 1 else 0 end";
sqlCmd = new SqlCommand(strCmd, conn);
return (int)sqlCmd.ExecuteScalar() == 1;
}
catch { return false; }
}
Dans le projet en cours sur mon travail, j'ai besoin d'écrire "agent de données" qui prendrait en charge de nombreux types de bases de données.
J'ai donc décidé de faire ensuite: écrire une classe de base avec la fonctionnalité de base (indépendante de la base de données) en utilisant des méthodes virtuelles et remplacer dans les sous-classes tous les moments spécifiques à la base de données
Si vous voulez éviter les solutions try-catch, je suggère cette méthode, en utilisant sys.tables
private bool IsTableExisting(string table)
{
string command = $"select * from sys.tables";
using (SqlConnection con = new SqlConnection(Constr))
using (SqlCommand com = new SqlCommand(command, con))
{
SqlDataReader reader = com.ExecuteReader();
while (reader.Read())
{
if (reader.GetString(0).ToLower() == table.ToLower())
return true;
}
reader.Close();
}
return false;
}
Très simple
use YOUR_DATABASE --OPTIONAL
SELECT count(*) as Exist from INFORMATION_SCHEMA.TABLES where table_name = 'YOUR_TABLE_NAME'
Si la réponse est 1, il y a un tableau. Si la réponse est 0, il n'y a pas de table.