Spl*_*iFF 9 sql postgresql plpgsql
我有一个查询,如果缺少某个列,我基本上需要一个回退值.我想知道我是否可以在我的查询中完全处理这个问题(而不是先探测并发送一个单独的查询.本质上我正在寻找一个等效的COALESCE处理丢失列的情况.
想象一下以下2个表.
T1
id | title | extra
1 A | value
- and -
T2
id | title
1 A
Run Code Online (Sandbox Code Playgroud)
我希望能够使用相同的查询从这些表中的任何一个中进行选择.
例如,如果t2实际上有一个我可以使用的"额外"列
SELECT id,title, COALESCE(extra, 'default') as extra
Run Code Online (Sandbox Code Playgroud)
但是只有在列值为NULL时才有效,而不是在列完全丢失时.
我更喜欢SQL版本,但我也可以接受PLPGSQL函数(行为类似于COALLESCE).
SQL纯粹主义者的注意事项:我真的不想辩论为什么我想在SQL中而不是在应用程序逻辑中这样做(或者为什么我不会只是将列永久地添加到模式中)所以请限制你的评论/答案具体要求,而不是你对数据库"正确性"的看法,或者其他任何可能会冒犯你的问题.
Erw*_*ter 14
SELECT id, title, CASE WHEN extra_exists THEN extra::text
ELSE 'default'::text END AS extra
FROM mytable
CROSS JOIN (
SELECT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'mytable'
AND column_name = 'extra') AS extra_exists
) AS extra
Run Code Online (Sandbox Code Playgroud)
通常,它根本不起作用.如果任何涉及的列不存在,Postgres将解析SQL语句并抛出异常.
诀窍是引入一个表名(或别名),其名称与相关列名相同.extra在这种情况下.每个表名都可以作为一个整体引用,这会导致整行返回为类型record.由于每种类型都可以投射text,我们可以将整个记录投射到text.这样,Postgres接受查询为有效.
由于列名优先于表名,extra::text因此mytable.extra如果列存在,则将其解释为列.否则,它将默认返回表的整行extra- 这从未发生过.
尝试选择一个不同的表别名,extra以便自己查看.
如果Postgres决定改变未来版本中计划的SQL字符串解析方式,那么这是一个未记录的黑客攻击并且可能会破坏 - 尽管这似乎不太可能.
如果你决定使用它,至少要明确它.
仅表格名称不是唯一的.名为"mytable"的表可以在同一数据库的多个模式中存在任意次数,这可能导致非常混乱和完全错误的结果.您还需要提供架构名称:
SELECT id, title, CASE WHEN col_exists THEN extra::text
ELSE 'default'::text END AS extra
FROM mytable
CROSS JOIN (
SELECT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'mytable'
AND column_name = 'extra'
) AS col_exists
) extraRun Code Online (Sandbox Code Playgroud)
由于此查询几乎无法移植到其他RDBMS,我建议使用目录表pg_attribute而不是信息架构视图information_schema.columns.大约快10倍.
SELECT id, title, CASE WHEN col_exists THEN extra::text
ELSE 'default'::text END AS extra
FROM mytable
CROSS JOIN (
SELECT EXISTS (
SELECT 1
FROM pg_catalog.pg_attribute
WHERE attrelid = 'myschema.mytable'::regclass -- schema-qualified!
AND attname = 'extra'
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
) AS col_exists
) extra;
Run Code Online (Sandbox Code Playgroud)
还使用更方便和安全的演员regclass - 这里详细解释:
regclass在Postgresql中表示什么
您可以将所需的别名附加到任何表,包括主表本身,以愚弄Postgres .您根本不需要加入另一个关系,这应该是最快的:
SELECT id, title, CASE WHEN EXISTS (
SELECT 1
FROM pg_catalog.pg_attribute
WHERE attrelid = 'mytable'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0
) THEN extra::text ELSE 'default'::text END AS extra
FROM mytable AS extra;Run Code Online (Sandbox Code Playgroud)
您可以在一个简单的SQL函数(一次)中封装测试存在,到达(几乎)到您一直要求的函数:
CREATE OR REPLACE FUNCTION col_exists(_tbl regclass, _col text)
RETURNS bool AS
$func$
SELECT EXISTS (
SELECT 1
FROM pg_catalog.pg_attribute
WHERE attrelid = $1
AND attname = $2
AND NOT attisdropped
AND attnum > 0
)
$func$
LANGUAGE sql STABLE;
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!)';
Run Code Online (Sandbox Code Playgroud)
将查询简化为:
SELECT id, title, CASE WHEN col_exists THEN extra::text
ELSE 'default'::text END AS extra
FROM mytable
CROSS JOIN col_exists('mytable', 'extra') AS extra(col_exists);
Run Code Online (Sandbox Code Playgroud)
在这里使用具有附加关系的表单,因为事实证明该功能更快.
但是,您只能使用任何这些查询获取列的文本表示.获得实际类型并不简单.
我在pg 9.1和9.2上运行了一个带有10万行的快速基准测试,以发现这些最快:
-- fastest
SELECT id, title, CASE WHEN EXISTS (
SELECT 1
FROM pg_catalog.pg_attribute
WHERE attrelid = 'mytable'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0
) THEN extra::text ELSE 'default'::text END AS extra
FROM mytable AS extra;
-- 2nd fastest
SELECT id, title, CASE WHEN col_exists THEN extra::text
ELSE 'default'::text END AS extra
FROM mytable
CROSS JOIN col_exists('mytable', 'extra') AS extra(col_exists);
Run Code Online (Sandbox Code Playgroud)
一种方法是查找信息模式表并用它做一些魔术。
就像是:
SELECT id, title, CASE WHEN extra_exists THEN extra ELSE 'default' END AS extra
FROM mytable
CROSS JOIN (
SELECT EXISTS (SELECT 1
FROM information_schema.columns
WHERE table_name='mytable' AND column_name='extra') AS extra_exists) extra
Run Code Online (Sandbox Code Playgroud)
编辑:需要为要查询的表传入 'mytable' 的位置。
| 归档时间: |
|
| 查看次数: |
7003 次 |
| 最近记录: |