Проверьте, существует ли таблица SQL - PullRequest
39 голосов
/ 21 января 2009

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

63 голосов
/ 21 января 2009
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;
    }
}
10 голосов
/ 21 января 2009

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

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

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

Например:

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

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

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

9 голосов
/ 21 января 2009

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

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

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

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

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

4 голосов
/ 21 января 2009

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

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

4 голосов
/ 21 января 2009

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

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

SELECT 1 FROM my_table WHERE 1=2
2 голосов
/ 14 декабря 2016

Мне хорошо подходит следующее ...

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; }
}
2 голосов
/ 21 января 2009

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

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

0 голосов
/ 29 декабря 2017

Очень просто

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

Если ответ 1, есть таблица. Если ответ 0, нет таблицы.

0 голосов
/ 11 сентября 2017

Если вы хотите избежать решений 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;
    }
...