Як перевірити, чи існує таблиця у заданій схемі


149

Постгреси 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 або новішої версії.

Відповіді:


284

Це залежить від того, що ви хочете точно протестувати .

Інформаційна схема?

Щоб знайти "чи існує таблиця" ( незалежно від того, хто запитує ), запит на інформаційну схему ( information_schema.tables) неправильний , строго кажучи, тому що ( за документацією ):

Показано лише ті таблиці та представлення, до яких поточний користувач має доступ (як власник або певний привілей).

Запит, наданий @kong, може повернутися FALSE, але таблиця все ще може існувати. Він відповідає на питання:

Як перевірити, чи існує таблиця (або представлення) і чи має поточний користувач доступ до неї?

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

Інформаційна схема в основному корисна для того, щоб залишатися переносними для основних версій та для різних RDBMS. Але реалізація відбувається повільно, оскільки 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' ]]
brauliobo

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+$'.
Erwin Brandstetter

1
@PeterKrauss Ви отримаєте цю помилку, якщо спробуєте скористатися функцією to_regclass у версії постгресів, старших 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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.