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

149

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

public
company1
company2
company3
...
companynn

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

search_path='company3,public'

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

например:

select isSpecific('company3','tablenotincompany3schema')

должен вернуться false, и

select isSpecific('company3','tableincompany3schema')

должен вернуться true.

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

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

Андрус
источник

Ответы:

284

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

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

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

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

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

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

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

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

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

Ваш вопрос был:

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

SELECT EXISTS (
   SELECT 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 FROM pg_tables
   WHERE  schemaname = 'schema_name'
   AND    tablename  = 'table_name'
   );

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

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

SELECT EXISTS (
   SELECT 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');

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

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

Эрвин Брандштеттер
источник
4
из оболочки:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
Браулиобо
1
Есть ли причина, по которой вы не используете pg_tables ?
m0meni
1
pg_tablesна самом деле хорошая идея для "Как проверить, существует ли таблица?" (Проверка таблиц только ., А не для других целей, как объяснено выше Кроме того , pg_tablesвид с участием нескольких таблиц ( pg_class, pg_namespace, pg_tablespace), который немного дороже Самая важная причина:. Я привык к запросу pg_classнепосредственно и не подумайте, pg_tablesкогда пишете этот ответ. Я добавил его выше, спасибо.
Эрвин Брандштеттер
1
@ sage88: Хорошо, я удалил свой неправильный комментарий. Вы можете использовать, pg_my_temp_schema()чтобы получить OID фактической временной схемы, если она существует. (Но в представлениях information_schemaнет OID. Вы могли бы SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) Ваш тест имеет несколько недостатков. Правильный тест будет table_schema LIKE 'pg\_temp\_%'более строгим table_schema ~ '^pg_temp_\d+$'.
Эрвин Брандштеттер
1
@PeterKrauss Вы получите эту ошибку, если попытаетесь использовать функцию to_regclass в версии postgres старше 9.4. Должно быть 9.4+
spetz83
44

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

SELECT EXISTS(
    SELECT * 
    FROM information_schema.tables 
    WHERE 
      table_schema = 'company3' AND 
      table_name = 'tableincompany3schema'
);
конг
источник
0

Для 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;
Питер Краусс
источник