Защо хаква Rowan работа (предимно)?
SELECT id, title
, CASE WHEN extra_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_name = 'tbl'
AND column_name = 'extra')
) AS extra(extra_exists)
Обикновено това изобщо няма да работи. Postgres анализира SQL израза и хвърля изключение, ако има такова от включените колони не съществува.
Номерът е да въведете име на таблица (или псевдоним) със същото име като името на въпросната колона. extra
в такъв случай. Всяко име на таблица може да бъде препратено като цяло, което води до връщане на целия ред като тип record
. И тъй като всеки тип може да бъде преобразуван в text
, можем да прехвърлим целия този запис към text
. По този начин Postgres приема заявката за валидна.
Тъй като имената на колони имат предимство пред имената на таблици, extra::text
се интерпретира като колоната tbl.extra
ако колоната съществува. В противен случай по подразбиране ще се върне целият ред на таблицата extra
- което никога не се случва.
Опитайте да изберете различен псевдоним на таблица за extra
за да видите сами.
Това е недокументиран хак и може да се повреди ако Postgres реши да промени начина, по който SQL низовете се анализират и планира в бъдещи версии - въпреки че това изглежда малко вероятно.
Недвусмислено
Ако решите да използвате това, поне направете го недвусмислено .
Самото име на таблица не е уникално. Таблица с име "tbl" може да съществува произволен брой пъти в множество схеми на една и съща база данни, което може да доведе до много объркващи и напълно неверни резултати. Вие трябвате за да предоставите името на схемата допълнително:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'tbl'
AND column_name = 'extra'
) AS col_exists
) extra;
По-бързо
Тъй като тази заявка е трудно преносима към други RDBMS, предлагам да използвате каталожна таблица pg_attribute
вместо изглед на информационна схема information_schema.columns
. Около 10 пъти по-бързо.
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'myschema.tbl'::regclass -- schema-qualified!
AND attname = 'extra'
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
)
) extra(col_exists);
Също така използвайки по-удобното и сигурно прехвърляне към regclass
. Вижте:
Можете да прикачите необходимия псевдоним, за да заблудите Postgres към всеки таблица, включително самата основна таблица. Изобщо не е необходимо да се присъединявате към друга релация, което трябва да е най-бързо:
SELECT id, title
, CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0)
THEN extra::text
ELSE 'default' END AS extra
FROM tbl AS extra;
Удобство
You could encapsulate the test for existence in a simple SQL function (once), arriving (almost) at the function you have been asking for:
CREATE OR REPLACE FUNCTION col_exists(_tbl regclass, _col text)
RETURNS bool
LANGUAGE sql STABLE AS
$func$
SELECT EXISTS (
SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = $1
AND attname = $2
AND NOT attisdropped
AND attnum > 0
)
$func$;
COMMENT ON FUNCTION col_exists(regclass, text) IS
'Test for existence of a column. Returns TRUE / FALSE.
$1 .. exact table name (case sensitive!), optionally schema-qualified
$2 .. exact column name (case sensitive!)';
Опростява заявката до:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN col_exists('tbl', 'extra') AS extra(col_exists);
Използване на формата с допълнителна релация тук, тъй като се оказа по-бързо с функцията.
Все пак получавате само текстово представяне на колоната с някоя от тези заявки. Не е толкова лесно да получите действителния тип .
Бенчмарк
Проведох бърз сравнителен анализ със 100 000 реда на стр. 9.1 и 9.2, за да установя, че тези са най-бързи:
Най-бърз:
SELECT id, title
, CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0)
THEN extra::text
ELSE 'default' END AS extra
FROM tbl AS extra;
2-ро най-бързо:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN col_exists('tbl', 'extra') AS extra(col_exists);