有没有更简洁的方法将查询结果转换为类型?
Posted
技术标签:
【中文标题】有没有更简洁的方法将查询结果转换为类型?【英文标题】:Is there a more succinct way to cast query results to a type? 【发布时间】:2014-07-23 18:33:00 【问题描述】:我经常将查询结果转换为用户定义的类型。考虑这个简单的例子:
test=# create type test_type as (a int, b int);
CREATE TYPE
test=# create table test_table (a int, b int);
CREATE TABLE
test=# insert into test_table values (1,2);
INSERT 0 1
test=# select r::test_type from (select * from test_table t) as r;
r
-------
(1,2)
(1 row)
对于我的很多查询来说,有一个子查询是必要的,而且效果很好。但是,有时它是从表到类型的简单 1 对 1 映射,就像上面的示例一样。
有没有更简单的表达方式?
当我尝试对我来说显而易见的事情时,我得到了错误:
test=# select t::test_type from test_table t;
ERROR: cannot cast type test_table to test_type
LINE 1: select t::test_type from test_table t;
^
【问题讨论】:
考虑更新我的答案。 【参考方案1】:您可以为此使用带有通配符列扩展的 ROW
构造。
regress=> select ROW(t.*)::testtype from testtable t;
row
-------
(1,2)
(1 row)
【讨论】:
我试过(t.*)::test_type
假设 (t.*)
会返回一个 row
但没有注意到它没有。由于它不起作用,我没有费心尝试显式的row
构造。为什么(t.*)
不返回一行?我想这是一些编译问题。
我也尝试过 (t.*)::test_type 在我的“猜测”中思考同样的事情。我假设 (t.*) 也会返回一行。
我对 Craig 做了更多研究,来自postgresql.org/docs/9.3/static/sql-expressions.html 4.2.13 “当列表中有多个表达式时,关键字 ROW 是可选的”
@bhirt 是的...我认为在解析时 t.*
仍然是一个表达式。看起来仍然很奇怪。如果时间允许,我会调查一下,看看解析器和重写器到底在做什么。
@ClodoaldoNeto 在 Pg 中的通配符扩展有点 hacky。它本质上是解析阶段中途的宏扩展,这会导致一些有趣的奇怪现象。尽管如此,我希望 (t.*)::sometype
自己工作,但很惊讶它没有。【参考方案2】:
简单一点
select t::test_type
from (table test_table) t;
或创建自己的演员表
create function test_table_2_test_type (test_table_value test_table)
returns test_type as $$
select test_table_value.a, test_table_value.b;
$$ language sql;
create cast (test_table as test_type)
with function test_table_2_test_type (test_table);
select t::test_type
from test_table t;
t
-------
(1,2)
http://www.postgresql.org/docs/current/static/sql-createcast.html
【讨论】:
对于像我这样想知道TABLE test_table
示例的人来说,它只是shorthand for SELECT * FROM
。我也不知道您可以只在 SELECT 列表中列出表名,就好像它是这样的列,但是我从来没有遇到过需要...【参考方案3】:
铸造技术
或者您可以转换为 text
作为中间类型,因为 everything 都可以转换为 text
和来自 text
:
SELECT t::text::test_type FROM test_table t;
这是针对各种类似问题的“包罗万象”的临时解决方案,您知道类型是兼容的,但缺少直接转换。
ROW
构造函数作为 demonstrated by @Craig 在这种情况下更优雅。
创建像demonstrated by @Clodoaldo 这样的新演员表更适合您要经常使用的案例。
但是,更好的解决方案是消除问题。
简单的解决方案:只使用表格类型
对于演示的简单案例,根本不要创建额外的类型。使用隐式自动创建的类型test_table
。 Per documentation:
CREATE TABLE
还会自动创建一个数据类型,表示 表中一行对应的复合类型。
所以,只是:
CREATE TABLE test_table (a int, b int);
INSERT INTO test_table VALUES (1,2);
SELECT r FROM test_table r;
r
-------
(1,2)
但您问题中的演示可能只是一个简化。对于一般解决方案:
多个表共享同一类型
Postgres 被某些人称为 ORDBMS(对象关系数据库管理系统)是有原因的。使用类型表。
显式创建通用类型或重用“主”表的隐式类型。
这是..
.. 不是 inheritance.
.. 不同于CREATE TABLE t (LIKE master)
..不同于CREATE TABLE t AS SELECT * FROM master LIMIT 0
Per documentation:
OF
type_name创建一个类型表,该表从指定的复合类型(名称可选模式限定)中获取其结构。一个打字 表与其类型相关;例如,如果该表将被删除 类型被删除(
DROP TYPE ... CASCADE
)。创建类型表时,列的数据类型由底层复合类型决定,不指定 通过
CREATE TABLE
命令。但是CREATE TABLE
命令可以添加 表的默认值和约束,并且可以指定存储参数。
食谱
CREATE TYPE master AS (a int, b int);
或者
CREATE TABLE master (a int, b int);
然后使用该类型创建更多相同类型的表:
CREATE TABLE table1 OF master;
CREATE TABLE table2 OF master;
INSERT INTO table1 VALUES (1,2);
INSERT INTO table2 VALUES (1,2);
SELECT r FROM table1 r; -- returns composite type "table1"
SELECT r::master FROM table1 r; -- returns composite type "master"
复合类型master
、table1
、table2
100% 相同,可以自动相互转换。
【讨论】:
【参考方案4】:如果我不厌其烦地创建一个新类型,我想我会在 create table
语句中使用它。
create table test_table (t test_type);
insert into test_table values ((2, 3));
-- No cast needed here.
select * from test_table;
吨
测试类型
--
(2, 3)
-- No cast needed here.
select t from (select * from test_table) x;
吨
测试类型
--
(2, 3)
如果您必须即时制造“test_type”类型的值,请使用行构造函数和类型转换。我认为 "::" 语法比 CAST() 更简洁,但两者都有效。
select (2, 3)::test_type
排
测试类型
--
(2, 3)
【讨论】:
如果我没看错其他示例,这并不完全等同:您创建了一个包含单个多值列的表,但其他查询从多值列中获取一行- 列表。 我认为我回答的最后一部分“如果你必须制造一个类型为 'test_type' 的值”与其他人所说的类似。 (我使用文字;列的工作方式相同。)但我的主要观点是,在表定义中使用用户定义的类型可以完全避免类型转换的需要。 还不错;它确实带来了主要的缺点,即您 必须 引用该复合行类型,因为SELECT a FROM test_table
不再有效。您也不能轻松地创建引用各个字段的索引、约束等。
很容易在旁观者的眼中。如果有充分的理由拥有复合类型——比如内在的date
、timestamp
、point
等——那么select (t).a from test_table
的价格并不是很高付钱。 PostgreSQL 支持表达式索引,包括复合类型的字段。
@IMSoP:你们可能都对typed tables
感兴趣,以求两全其美。我在答案中添加了详细信息。以上是关于有没有更简洁的方法将查询结果转换为类型?的主要内容,如果未能解决你的问题,请参考以下文章