Как проверить, существует ли таблица в данной схеме

База данных Postgres 8.4 и выше содержит общие таблицы в таблице public и конкретных для компании таблицах в схеме company.
company имена схем всегда начинаются с 'company' и заканчиваются номером компании.
Таким образом, могут быть схемы:

public
company1
company2
company3
...
companynn

Приложение всегда работает с одной компанией.
search_path указан соответственно в строке соединения odbc или npgsql, например:

search_path='company3,public'

Как проверить, существует ли данная таблица в указанной схеме companyn?

select isSpecific('company3','tablenotincompany3schema')

должен возвращать false и

select isSpecific('company3','tableincompany3schema')

должен возвращать true.

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

Если данная таблица существует как в public, так и в переданной схеме, функция должна возвращать true.
Он должен работать для Postgres 8.4 или новее.

Ответ 1

Это зависит от того, что вы хотите проверить точно.

Информационная схема?

Чтобы найти "существует ли таблица" (независимо от того, кто спрашивает), запрос информационной схемы (information_schema.tables) строго , потому что (для документации):

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

Запрос продемонстрированный @kong, может вернуть FALSE, но таблица все еще может существовать. Он отвечает на вопрос:

Как проверить, существует ли таблица (или представление), и у текущего пользователя есть доступ к ней?

SELECT EXISTS (
   SELECT 1
   FROM   information_schema.tables 
   WHERE  table_schema = 'schema_name'
   AND    table_name = 'table_name'
   );

Информационная схема в основном полезна, чтобы оставаться переносной в основных версиях и в разных РСУБД. Но реализация выполняется медленно, потому что Postgres должен использовать сложные представления для соответствия стандарту (information_schema.tables - довольно простой пример). И некоторая информация (например, OID) теряется при переводе из системных каталогов, которые фактически несут всю информацию.

Системные каталоги

Ваш вопрос:

Как проверить, существует ли таблица?

SELECT EXISTS (
   SELECT 1 
   FROM   pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = 'schema_name'
   AND    c.relname = 'table_name'
   AND    c.relkind = 'r'    -- only tables
   );

Используйте системные каталоги pg_class и pg_namespace напрямую, что также значительно быстрее. Однако в документации по pg_class:

Каталог pg_class каталогизирует таблицы и большинство всего остального, что имеет столбцов или аналогично таблице. Это включает индексы (но см. также pg_index), последовательности, представления, материализованные представления, типы и таблицы TOAST;

Для этого конкретного вопроса вы также можете использовать системный вид pg_tables. Немного проще и более портативно в основных версиях Postgres (что вряд ли вызывает озабоченность в отношении этого базового запроса):

SELECT EXISTS (
   SELECT 1 
   FROM   pg_tables
   WHERE  schemaname = 'schema_name'
   AND    tablename = 'table_name'
   );

Идентификаторы должны быть уникальными среди всех объектов, упомянутых выше. Если вы хотите спросить:

Как проверить, принято ли имя для таблицы или аналогичного объекта в данной схеме?

SELECT EXISTS (
   SELECT 1 
   FROM   pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = 'schema_name'
   AND    c.relname = 'table_name'
   );

Альтернатива: добавлено к regclass

SELECT 'schema_name.table_name'::regclass

Это вызывает исключение, если таблица (необязательно схема) (или другой объект, занимающий это имя) не существует.

Если вы не присваиваете схеме имя таблицы, приведение в regclass по умолчанию соответствует search_path и возвращает OID для первой найденной таблицы - или исключение, если таблица не включена ни в одну из перечисленных схем. Обратите внимание, что системные схемы pg_catalog и pg_temp (схема для временных объектов текущего сеанса) автоматически входят в состав search_path.

Вы можете использовать это и уловить возможное исключение в функции. Пример:

Запрос, подобный выше, позволяет избежать возможных исключений и, следовательно, немного быстрее.

to_regclass(rel_name) в Postgres 9.4 +

Намного проще:

SELECT to_regclass('schema_name.table_name');

То же, что и приведение, , но возвращается...

... null, а не бросать ошибку, если имя не найдено

Ответ 2

Возможно, используйте information_schema:

SELECT EXISTS(
    SELECT * 
    FROM information_schema.tables 
    WHERE 
      table_schema = 'company3' AND 
      table_name = 'tableincompany3schema'
);

Ответ 3

Для PostgreSQL 9.3 или менее... Или кому нравится все нормализованное к тексту

Три аромата моей старой библиотеки SwissKnife: relname_exists(anyThing), relname_normalized(anyThing) и relnamechecked_to_array(anyThing). Все проверки из таблицы pg_catalog.pg_class и возвращают стандартные универсальные типы данных (логические, текстовые или текстовые []).

/**
 * From my old SwissKnife Lib to your SwissKnife. License CC0.
 * Check and normalize to array the free-parameter relation-name.
 * Options: (name); (name,schema), ("schema.name"). Ignores schema2 in ("schema.name",schema2).
 */
CREATE FUNCTION relname_to_array(text,text default NULL) RETURNS text[] AS $f$
     SELECT array[n.nspname::text, c.relname::text]
     FROM   pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace,
            regexp_split_to_array($1,'\.') t(x) -- not work with quoted names
     WHERE  CASE
              WHEN COALESCE(x[2],'')>'' THEN n.nspname = x[1]      AND c.relname = x[2]
              WHEN $2 IS NULL THEN           n.nspname = 'public'  AND c.relname = $1
              ELSE                           n.nspname = $2        AND c.relname = $1
            END
$f$ language SQL IMMUTABLE;

CREATE FUNCTION relname_exists(text,text default NULL) RETURNS boolean AS $wrap$
  SELECT EXISTS (SELECT relname_to_array($1,$2))
$wrap$ language SQL IMMUTABLE;

CREATE FUNCTION relname_normalized(text,text default NULL,boolean DEFAULT true) RETURNS text AS $wrap$
  SELECT COALESCE(array_to_string(relname_to_array($1,$2), '.'), CASE WHEN $3 THEN '' ELSE NULL END)
$wrap$ language SQL IMMUTABLE;