43

以数据库独立方式检查 Sql 数据库中是否存在表的最佳方法是什么?

我想出了:

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

有一个更好的方法吗?当与数据库的连接失败时,此方法将不起作用。我找到了适用于 Sybase、SQL server、Oracle 的方法,但没有任何方法适用于所有数据库。

4

9 回答 9

68
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;
    }
}
于 2009-01-21T09:03:05.310 回答
11

如果您正在尝试数据库独立性,您将不得不假设一个最低标准。IIRC ANSI INFORMATION_SCHEMA视图是 ODBC 一致性所必需的,因此您可以像这样查询它们:

select count (*) 
  from information_schema.tables 
 where table_name = 'foobar'

鉴于您使用的是 ODBC,您还可以使用各种ODBC API 调用来检索此元数据。

请记住,可移植性等同于在任何地方进行一次编写测试,因此您仍然必须在您打算支持的每个平台上测试应用程序。这意味着您天生受限于有限数量的可能数据库平台,因为您只有这么多资源用于测试。

结果是您需要为您的应用程序找到一个最低的公分母(这比寻找 SQL 困难得多)或构建一个平台相关部分,其中不可移植的功能可以插入到每个平台上基础。

于 2009-01-21T10:25:30.433 回答
10

我不认为存在一种适用于所有数据库的通用方式,因为这是非常具体的东西,取决于数据库的构建方式。

但是,为什么要使用特定查询来执行此操作?您不能将实现从您想做的事情中抽象出来吗?我的意思是:为什么不创建一个通用接口,其中包括一个名为“TableExists(string tablename)”的方法。然后,对于您想要支持的每个 DBMS,您创建一个实现该接口的类,并在 TableExists 方法中,为该 DBMS 编写特定的逻辑。
然后 SQLServer 实现将包含一个查询 sysobjects 的查询。

在您的应用程序中,您可以拥有一个为给定上下文创建正确实现的工厂类,然后您只需调用 TableExists 方法。

例如:

IMyInterface foo = MyFactory.CreateMyInterface (SupportedDbms.SqlServer);

if( foo.TableExists ("mytable") )
...

我认为这是我应该做的。

于 2009-01-21T08:57:57.707 回答
4

我完全支持 Frederik Gheysels 的回答。如果你必须支持多个数据库系统,你应该针对一个抽象接口实现你的代码,每个数据库系统都有特定的实现。除了检查现有表之外,还有更多不兼容语法的示例(例如:将查询限制为一定数量的行)。

但是,如果您确实必须使用示例中的异常处理来执行检查,您应该使用比 COUNT(*) 更有效的以下查询,因为数据库没有实际的选择工作要做:

SELECT 1 FROM my_table WHERE 1=2
于 2009-01-21T09:05:16.960 回答
4

我会避免执行,select count(x) from xxxxxx因为 DBMS 实际上会继续执行,这对于大表可能需要一些时间。

相反,只需准备一个select * from mysterytable查询。如果神秘表不存在,准备将失败。不需要实际执行准备好的语句。

于 2009-01-21T10:04:50.350 回答
3

以下对我很有效......

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; }
}
于 2016-12-14T18:40:21.750 回答
2

在我工作的当前项目中,我需要编写支持许多数据库类型的“数据代理”。

所以我决定下一步做:使用虚拟方法编写一个具有基本(数据库独立)功能的基类,并在子类中覆盖所有特定于数据库的时刻

于 2009-01-21T09:27:14.840 回答
1

非常简单

use YOUR_DATABASE --OPTIONAL
SELECT count(*) as Exist from INFORMATION_SCHEMA.TABLES where table_name = 'YOUR_TABLE_NAME'

如果答案是 1,则有一张桌子。如果答案为 0,则没有表。

于 2017-12-29T19:54:48.387 回答
0

如果你想避免 try-catch 解决方案,我建议使用这种方法,使用 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;
    }
于 2017-09-11T08:54:43.670 回答