Проверьте, существует ли таблица SQL

Каков наилучший способ проверить, существует ли таблица в базе данных 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, но ничего, что работает для всех баз данных.

9 ответов

Решение
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;
    }
}

Если вы пытаетесь получить независимость от базы данных, вам нужно принять минимальный стандарт. IIRC Представления ANSI INFORMATION_SCHEMA требуются для соответствия ODBC, поэтому вы можете запросить их, например:

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

Учитывая, что вы используете ODBC, вы также можете использовать различные вызовы ODBC API для извлечения этих метаданных.

Имейте в виду, что переносимость приравнивается к тестированию с однократной записью в любом месте, поэтому вам все равно придется тестировать приложение на каждой платформе, которую вы намереваетесь поддерживать. Это означает, что вы по своей сути ограничены конечным числом возможных платформ баз данных, поскольку у вас достаточно ресурсов для тестирования.

В результате вам нужно найти наименьший общий знаменатель для вашего приложения (что гораздо сложнее, чем выглядит для SQL) или построить зависимый от платформы раздел, в котором непереносимые функции могут быть подключены к каждой платформе. основа.

Я не думаю, что существует один общий способ, который работает для всех баз данных, поскольку это что-то очень специфическое, что зависит от того, как построена БД.

Но почему вы хотите сделать это, используя определенный запрос? Не можете ли вы абстрагировать реализацию от того, что вы хотите сделать? Я имею в виду: почему бы не создать универсальный интерфейс, в котором есть, например, метод с именем TableExists( string tablename). Затем для каждой СУБД, которую вы хотите поддерживать, вы создаете класс, который реализует этот интерфейс, а в методе TableExists вы пишете специальную логику для этой СУБД.
Реализация SQLServer будет тогда содержать запрос, который запрашивает системные объекты.

В вашем приложении вы можете иметь фабричный класс, который создает правильную реализацию для данного контекста, а затем вы просто вызываете метод TableExists.

Например:

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

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

Я думаю, это то, как я должен это сделать.

Я полностью поддерживаю ответ Фредерика Гейселя. Если вам необходимо поддерживать несколько систем баз данных, вы должны реализовать свой код на основе абстрактного интерфейса с конкретными реализациями для системы баз данных. Примеров несовместимого синтаксиса гораздо больше, чем просто проверка существующей таблицы (например, ограничение запроса определенным количеством строк).

Но если вам действительно нужно выполнить проверку с использованием обработки исключений из вашего примера, вам следует использовать следующий запрос, который более эффективен, чем COUNT(*), поскольку в базе данных нет фактической работы по выбору:

SELECT 1 FROM my_table WHERE 1=2

Я бы не стал выполнять select count(x) from xxxxxx поскольку СУБД будет на самом деле идти вперед и делать это, что может занять некоторое время для большой таблицы.

Вместо этого просто подготовить select * from mysterytable запрос. Подготовка потерпит неудачу, если таинственное не существует. Нет необходимости фактически выполнять подготовленный оператор.

Следующее работает хорошо для меня...

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

В текущем проекте на моей работе мне нужно написать "агент данных", который бы поддерживал множество типов баз данных.

Поэтому я решил сделать следующее: написать базовый класс с базовой (независимой от базы данных) функциональностью, используя виртуальные методы, и переопределить в подклассах все специфичные для базы данных моменты.

Очень просто

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

Если ответ 1, есть таблица. Если ответ 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;
    }
Другие вопросы по тегам