【问题标题】:Check if Database Exists Before Creating创建前检查数据库是否存在
【发布时间】:2011-01-14 23:46:06
【问题描述】:

这看起来很琐碎,但现在让我很沮丧。

我在 SQL Server 2005 Express 中使用 C#。

我正在使用以下代码。我想在创建数据库之前检查它是否存在。但是,返回的整数是 -1,这也是 MSDN 定义 ExecuteNonQuery() 将返回的内容的方式。现在,数据库确实存在,但它仍然返回 -1。话虽如此,我怎样才能使这项工作获得理想的结果?

private static void checkInventoryDatabaseExists(ref SqlConnection tmpConn, ref bool databaseExists)
{
    string sqlCreateDBQuery;
    try
    {
        tmpConn = new SqlConnection("server=(local)\\SQLEXPRESS;Trusted_Connection=yes");

        sqlCreateDBQuery = "SELECT * FROM master.dbo.sysdatabases where name = 
        \'INVENTORY\'";

        using (tmpConn)
        {
            tmpConn.Open();
            tmpConn.ChangeDatabase("master");

            using (SqlCommand sqlCmd = new SqlCommand(sqlCreateDBQuery, tmpConn))
            {
                int exists = sqlCmd.ExecuteNonQuery();

                if (exists <= 0)
                    databaseExists = false;
                else
                    databaseExists = true;
            }
        }
    }
    catch (Exception ex) { }

}

【问题讨论】:

    标签: c# sql-server-2005 tsql


    【解决方案1】:

    从 SQL Server 2005 开始,旧式 sysobjectssysdatabases 以及这些目录视图已被弃用。改为这样做 - 使用 sys. 模式 - 像 sys.databases 这样的视图

    private static bool CheckDatabaseExists(SqlConnection tmpConn, string databaseName)
    {
        string sqlCreateDBQuery;
        bool result = false;
    
        try
        {
            tmpConn = new SqlConnection("server=(local)\\SQLEXPRESS;Trusted_Connection=yes");
    
            sqlCreateDBQuery = string.Format("SELECT database_id FROM sys.databases WHERE Name 
            = '{0}'", databaseName);
    
            using (tmpConn)
            {
                using (SqlCommand sqlCmd = new SqlCommand(sqlCreateDBQuery, tmpConn))
                {
                    tmpConn.Open();
    
                    object resultObj = sqlCmd.ExecuteScalar();
    
                    int databaseID = 0;    
    
                    if (resultObj != null)
                    {
                        int.TryParse(resultObj.ToString(), out databaseID);
                    }
    
                    tmpConn.Close();
    
                    result = (databaseID > 0);
                }
            }
        } 
        catch (Exception ex)
        { 
            result = false;
        }
    
        return result;
    }
    

    这将适用于您作为参数传入的任何数据库名称,它将返回 bool true = 数据库存在,false = 数据库不存在(或发生错误)。

    【讨论】:

    • 现在尝试.. executescalar 返回对象,因此您必须在分配之前进行强制转换。
    • 它抛出“无效的列名'INVENTORY'”的异常
    • 抱歉,是的 - 数据库名称需要用单引号括起来 - 更新了我的答案
    • 这很好用。如果它不存在,它会抛出“对象引用未设置为对象的实例。”否则它返回一个非零的数据库 id!
    • 只是想知道为什么你不能在这里使用DB_ID?不太容易出错,不是吗? (看起来是其他人提交的 - 我很好奇您选择这种方式是否有原因)
    【解决方案2】:

    几年后阅读这篇文章,有一种更简洁的表达方式:

    public static bool CheckDatabaseExists(string connectionString, string databaseName)
    {
          using (var connection = new SqlConnection(connectionString))
          {
               using (var command = new SqlCommand($"SELECT db_id('{databaseName}')", connection))
               {
                    connection.Open();
                    return (command.ExecuteScalar() != DBNull.Value);
               }
          }
    }
    

    【讨论】:

    • 这里提防sql注入,使用参数化查询代替字符串插值
    • 但是 db_id 是 SQL Server T-SQL 函数,所以我在这里看不到 sql 注入有任何问题。参考docs.microsoft.com/en-us/sql/t-sql/functions/…
    • 这似乎很耗时
    • 我发现的一件事是你在这里使用的连接字符串不能指定你正在检查的数据库。如果你这样做了,那么connection.Open() 会在不存在的情况下抛出异常。
    【解决方案3】:

    不应该这样

    "SELECT * FROM master.dbo.sysdatabases where name = \'INVENTORY\'"
    

    是这个吗?

    "SELECT * FROM master.dbo.sysdatabases where name = 'INVENTORY'"
    

    也根据 MSDN

    对于 UPDATE、INSERT 和 DELETE 语句,返回值是受命令影响的行数。当正在插入或更新的表上存在触发器时,返回值包括受插入或更新操作影响的行数以及受一个或多个触发器影响的行数。对于所有其他类型的语句,返回值为 -1。如果发生回滚,返回值也是-1。

    您正在执行 SELECT 而不是 DML 语句。为什么不使用 ExecuteReader 方法呢?

    【讨论】:

    • 我会用 ExecuteReader 代替,因为你不做 DML
    【解决方案4】:

    查询系统视图的另一种方法是使用函数 db_​​id,如果数据库存在则返回数据库的 ID,否则返回 null。下面的 T-SQL 示例:

    if (db_id('INVENTORY') is null)
    begin
        return 0
    end
    else
    begin
        return 1
    end
    

    【讨论】:

      【解决方案5】:

      您不能使用 ExecuteNonQuery,因为它总是会为 SELECT 返回 -1,如 MSDN 链接所示。

      您必须使用处理结果集,例如 SELECT DB_ID('INVENTORY') AS DatabaseID 或使用变量/参数:SELECT @DatabaseID = DB_ID('INVENTORY')

      【讨论】:

        【解决方案6】:

        采用 Stephen Lloyd 的代码并添加了一些异步和 sql 注入缓解措施。

        public static async Task<bool> TestDatabase(string connectionString, string databaseName)
        {
           using (var connection = new SqlConnection(connectionString))
           using (var command = new SqlCommand("SELECT db_id(@databaseName)", connection))
           {
               command.Parameters.Add(new SqlParameter("databaseName", databaseName));
        
               connection.Open();
        
               return (await command.ExecuteScalarAsync() != DBNull.Value);
           }
        }
        

        【讨论】:

        • Open 是否有异步过载?
        【解决方案7】:

        为了搜索者的利益,如果您使用实体框架,this 将起作用:

        using (var ctx = new MyDataModel())
        {
            dbExists = System.Data.Entity.Database.Exists(ctx.Database.Connection);
        }
        

        【讨论】:

          【解决方案8】:

          使用此程序集:Microsoft.SqlServer.SqlManagementObjects => NuGet

          using Microsoft.SqlServer.Management.Smo;
          
          var dbExists = new Server(serverOrInstanceName).Databases.Contains(dataBaseName);
          

          【讨论】:

            猜你喜欢
            • 2019-12-05
            • 1970-01-01
            • 2016-08-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-09-22
            • 2021-03-20
            • 1970-01-01
            相关资源
            最近更新 更多