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

Лучшим способом было бы использовать «ВЫБРАТЬ 1 ИЗ tbl, ГДЕ 1 = 0». Таким образом, это не будет потреблять много ресурсов.

Alex Shnayder 21.01.2009 12:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
43
1
80 396
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

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

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

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

Например:

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

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

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

Именно так мы делаем это в нашем основном приложении. Однако что, если у вас есть только odbc-соединение и вы не знаете, какая база данных за ним?

Carra 21.01.2009 12:17

Мой опыт подсказывает, что это неправильный путь! Возможно, вы получите нулевую производительность, к сожалению

abatishchev 21.01.2009 12:29

@abtischev - не могли бы вы поподробнее?

MPritchard 11.06.2010 10:59
Ответ принят как подходящий
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;
    }
}

См. Эту ссылку msdn.microsoft.com/en-us/library/ms186778.aspx для получения дополнительной информации о information_schema в Sql-Server.

GvS 21.01.2009 13:25

-1 от меня. Это не работает в MySql. Coz, он возвращает true, если в какой-либо базе данных есть таблица с именем tableName. Я тестировал это с помощью MySql5.1 + Navicat8.

user366312 30.04.2010 21:41

@JMSA: извините, я забыл включить schema_name (поле имени базы данных). select * from information_schema.tables where schema_name = 'yourDatabaseNameHere' and table_name = 'yourTableNameHere. любезно отмените голос против

Michael Buen 01.05.2010 04:26

Это тоже не сработает. Coz, Navicat показывает, что в таблице information_schema.tables нет поля с именем schema_name. Существует поле с именем table_schema, и в каждую строку этого поля вставлен нуль.

user366312 02.05.2010 12:52

@JMSA: это моя вина, если определенная база данных не совместима с ANSI SQL? возможно, вам следует внести свой вклад в решение, а не резко отклонять голосование только потому, что мой ответ не охватывает всю базу данных

Michael Buen 02.05.2010 15:03

это не schema_name, это table_schema, я должен был скопировать и вставить здесь то, что я тестировал на своей установке Mysql. это должно работать в вашей командной строке MySQL или программно, попробуйте запрос ниже здесь, он работает, иначе это может быть проблема Navicat: select table_schema, table_name from information_schema.tables where table_schema = 'database_name_here' and table_name = 'table_name_here';

Michael Buen 02.05.2010 15:26

Я получил ошибку ExecuteScalar: Connection property has not been initialized, потому что забыл назначить соединение: cmd.Connection = mDbConnection;. Это можно сделать и в конструкторе OdbcCommand.

Peopleware 25.06.2015 14:15

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

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

SELECT 1 FROM my_table WHERE 1=2

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

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

Определенно способ сделать это, да.

Nyerguds 28.04.2016 13:56

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

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

Я не знаю насчет ODBC, но в Oracle, использующем JDBC, вы можете подготовить оператор, который полностью выйдет из строя, когда вы его запустите.

Mr. Shiny and New 安宇 21.01.2009 17:40

Да, но он хочет изменить только в том случае, если существует таблица, которая будет подтверждена подготовкой. Особенно, если это простой "Выбрать * из ?????" который может только потерпеть неудачу, если ??? не существует.

James Anderson 21.01.2009 18:45

Я действительно сомневаюсь, что "select count (*)" (без предложения where) займет много времени для большой таблицы - я бы предположил, что все базы данных хранят количество строк во внутренних базах данных / индексах / кешах, поэтому это будет просто один уважать. Таким образом, размер таблицы не будет иметь отношения ко времени выполнения запроса.

David_001 11.06.2010 11:09

@David - зависит от СУБД, но большинство из них не ведет точного подсчета строк и фактически подсчитывает строки (или, по крайней мере, записи в индексе PK). Если вы думаете об этом, компромисс между поддержанием «счетчика», который был бы предметом спора для каждой вставки или удаления, и оптимизацией сравнительно редкого оператора выбора, является хорошим.

James Anderson 14.06.2010 09:51

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

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

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

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

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

Тогда, если я правильно понимаю ваш пост, каждая СУБД должна иметь представление INFORMATION_SCHEMA в соответствии с каким-то стандартом?

Frederik Gheysels 21.01.2009 22:38

Я считаю, что представления INFORMATION_SCHEMA необходимы для соответствия стандарту ANSI SQL-92. Однако поставщики СУБД, как правило, немного поспешно используют стандарты ANSI SQL в своих заявлениях о соответствии.

ConcernedOfTunbridgeWells 25.01.2009 20:29

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

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

Если вы хотите избежать решений 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 (и используйте параметризацию, избегая конкатенации строк при построении запросов!). Пожалуйста, подумайте о безопасности!

Elaskanator 15.10.2018 20:05

Очень простой

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

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

Другие вопросы по теме