【问题标题】:Check if a SQL table exists检查是否存在 SQL 表
【发布时间】:2010-10-02 15:27:05
【问题描述】:

以数据库独立方式检查表是否存在于 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 服务器、Oracle 的方法,但没有任何方法适用于所有数据库。

【问题讨论】:

  • 更好的方法是使用 "SELECT 1 FROM tbl WHERE 1=0" 这样就不会消耗资源。

标签: c# .net sql ado.net odbc


【解决方案1】:
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;
    }
}

【讨论】:

  • 有关 Sql-Server 中 information_schema 的更多信息,请参阅此链接 msdn.microsoft.com/en-us/library/ms186778.aspx
  • -1 来自我。这在 MySql 中不起作用。因为,如果任何数据库有一个名为“tableName”的表,它就会返回 true。我已经用 MySql5.1 + Navicat8 对此进行了测试。
  • @JMSA:抱歉,我忘了包含 schema_name(数据库名称字段)。 select * from information_schema.tables where schema_name = 'yourDatabaseNameHere' and table_name = 'yourTableNameHere。请撤消投票
  • 这也行不通。因为,Navicat 显示,在 'information_schema.tables' 表中没有名为 'schema_name' 的字段。有一个名为“table_schema”的字段,并且在该字段的每一行中都插入了空值。
  • @JMSA:如果某个数据库不符合 ANSI SQL,那会是我的错吗?也许您应该提供解决方案,而不是仅仅因为我的回答没有涵盖所有数据库而突然投反对票
【解决方案2】:

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

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

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

请记住,可移植性等同于write-once test anywhere,因此您仍然需要在您打算支持的每个平台上测试应用程序。这意味着您天生受限于有限数量的可能数据库平台,因为您只有这么多资源用于测试。

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

【讨论】:

  • 那么,如果我正确理解了您的帖子,那么根据某些标准,每个 DBMS 都应该有一个 INFORMATION_SCHEMA 视图?
  • 我相信 INFORMATION_SCHEMA 视图是符合 ANSI SQL-92 标准所必需的。但是,DBMS 供应商在其合规性声明中倾向于对 ANSI SQL 标准进行快速而松散的处理。
【解决方案3】:

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

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

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

例如:

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

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

我认为我应该这样做。

【讨论】:

  • 这就是我们在主应用程序中执行此操作的方式。但是,如果您只有一个 odbc 连接并且不知道它背后的数据库是什么?
  • 我的经验告诉我这是错误的方法!不幸的是,你可能会得到零性能
  • @abtischev - 你能说得更详细些吗?
【解决方案4】:

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

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

SELECT 1 FROM my_table WHERE 1=2

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

    • 我不知道 ODBC,但是在使用 JDBC 的 Oracle 上,您可以准备一个运行时完全失败的语句。
    • 是的——但他只想检查准备验证的表是否存在。特别是如果它是简单的“从?????中选择*​​”只有在 ??? 时才会失败不存在。
    • 我真的怀疑“select count(*)”(没有 where 子句)对于一个大表会花费很长时间 - 我想所有数据库都将行数存储在内部数据库/索引/缓存中,所以它只是一个单一的查找。因此,表的大小与查询运行的时间无关。
    • @David -- 取决于 DBMS,但大多数不会保持准确的行数,而是会实际计算行数(或至少 PK 索引中的条目)。如果您考虑一下,在维护“计数”(这将是每个插入或删除的争论点)与优化相对稀有的选择语句之间进行权衡是一个很好的选择。
    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

      • 绝对是这样做的方法,是的。
      【解决方案8】:

      很简单

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

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

      【讨论】:

        【解决方案9】:

        如果你想避免 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;
            }
        

        【讨论】:

        • 您不应假设服务器具有不区分大小写的对象名称(比较小写形式),也不应强制服务器返回每个表的列表。在 SQL Server 中,只需检查 OBJECT_ID(@tableName) 是否返回 NULL(并使用参数化,在构建查询时避免字符串连接!)。请注意安全!
        猜你喜欢
        • 2011-07-21
        • 1970-01-01
        • 2010-09-15
        • 2019-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-12
        相关资源
        最近更新 更多