PostgreSQL 自定义自动类型转换操作(CAST)

背景

PostgreSQL是一个强类型数据库,因此你输入的变量、常量是什么类型,是强绑定的,例如

在调用操作符时,需要通过操作符边上的数据类型,选择对应的操作符。

在调用函数时,需要根据输入的类型,选择对应的函数。

如果类型不匹配,就会报操作符不存在,或者函数不存在的错误。

postgres=# select '1' + '1';
ERROR: operator is not unique: unknown + unknown
LINE 1: select '1' + '1';
     ^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts. 

那么使用起来是不是很不方便呢?

PostgreSQL开放了类型转换的接口,同时也内置了很多的自动类型转换。来简化操作。

查看目前已有的类型转换:

postgres=# \dC+
            List of casts
   Source type   |   Target type   |  Function  | Implicit? | Description
-----------------------------+-----------------------------+--------------------+---------------+-------------
 "char"      | character     | bpchar    | in assignment |
 "char"      | character varying   | text    | in assignment |
 "char"      | integer      | int4    | no   |
 "char"      | text      | text    | yes   |
 abstime      | date      | date    | in assignment |
 abstime      | integer      | (binary coercible) | no   |
 abstime      | time without time zone  | time    | in assignment | 

 ................................ 

 timestamp without time zone | timestamp with time zone | timestamptz  | yes   |
 timestamp without time zone | timestamp without time zone | timestamp   | yes   |
 xml       | character     | (binary coercible) | in assignment |
 xml       | character varying   | (binary coercible) | in assignment |
 xml       | text      | (binary coercible) | in assignment |
(246 rows) 

如果你发现有些类型转换没有内置,怎么办呢?我们可以自定义转换。

当然你也可以使用这种语法,对类型进行强制转换:

CAST(x AS typename) 

 or 

x::typename 

如何自定义类型转换(CAST)

自定义CAST的语法如下:

CREATE CAST (source_type AS target_type)
 WITH FUNCTION function_name [ (argument_type [, ...]) ]
 [ AS ASSIGNMENT | AS IMPLICIT ] 

CREATE CAST (source_type AS target_type)
 WITHOUT FUNCTION
 [ AS ASSIGNMENT | AS IMPLICIT ] 

CREATE CAST (source_type AS target_type)
 WITH INOUT
 [ AS ASSIGNMENT | AS IMPLICIT ] 

解释:

1、WITH FUNCTION,表示转换需要用到什么函数。

2、WITHOUT FUNCTION,表示被转换的两个类型,在数据库的存储中一致,即物理存储一致。例如text和varchar的物理存储一致。不需要转换函数。

Two types can be binary coercible,
which means that the conversion can be performed “for free” without invoking any function. 

This requires that corresponding values use the same internal representation. 

For instance, the types text and varchar are binary coercible both ways. 

Binary coercibility is not necessarily a symmetric relationship. 

For example, the cast from xml to text can be performed for free in the present implementation,
but the reverse direction requires a function that performs at least a syntax check. 

(Two types that are binary coercible both ways are also referred to as binary compatible.) 

3、WITH INOUT,表示使用内置的IO函数进行转换。每一种类型,都有INPUT 和OUTPUT函数。使用这种方法,好处是不需要重新写转换函数。

除非有特殊需求,我们建议直接使用IO函数来进行转换。

        List of functions
 Schema |  Name  | Result data type | Argument data types | Type
------------+-----------------+------------------+---------------------+--------
 pg_catalog | textin   | text    | cstring    | normal
 pg_catalog | textout   | cstring   | text    | normal
 pg_catalog | date_in   | date    | cstring    | normal
 pg_catalog | date_out  | cstring   | date    | normal
You can define a cast as an I/O conversion cast by using the WITH INOUT syntax. 

An I/O conversion cast is performed by invoking the output function of the source data type,
and passing the resulting string to the input function of the target data type. 

In many common cases, this feature avoids the need to write a separate cast function for conversion. 

An I/O conversion cast acts the same as a regular function-based cast; only the implementation is different. 

4、AS ASSIGNMENT,表示在赋值时,自动对类型进行转换。例如字段类型为TEXT,输入的类型为INT,那么可以创建一个 cast(int as text) as ASSIGNMENT。

If the cast is marked AS ASSIGNMENT then it can be invoked implicitly when assigning a value to a column of the target data type. 

For example, supposing that foo.f1 is a column of type text, then: 

INSERT INTO foo (f1) VALUES (42); 

will be allowed if the cast from type integer to type text is marked AS ASSIGNMENT,
otherwise not. 

(We generally use the term assignment cast to describe this kind of cast.) 

5、AS IMPLICIT,表示在表达式中,或者在赋值操作中,都对类型进行自动转换。(包含了AS ASSIGNMENT,它只对赋值进行转换)

If the cast is marked AS IMPLICIT then it can be invoked implicitly in any context,
whether assignment or internally in an expression. 

(We generally use the term implicit cast to describe this kind of cast.) 

For example, consider this query: 

SELECT 2 + 4.0; 

The parser initially marks the constants as being of type integer and numeric respectively. 

There is no integer + numeric operator in the system catalogs, but there is a numeric + numeric operator. 

The query will therefore succeed if a cast from integer to numeric is available and is marked AS IMPLICIT —
which in fact it is. 

The parser will apply the implicit cast and resolve the query as if it had been written 

SELECT CAST ( 2 AS numeric ) + 4.0; 

6、注意,AS IMPLICIT需要谨慎使用,为什么呢?因为操作符会涉及到多个算子,如果有多个转换,目前数据库并不知道应该选择哪个?

Now, the catalogs also provide a cast from numeric to integer. 

If that cast were marked AS IMPLICIT — (which it is not — ) 

then the parser would be faced with choosing between the above interpretation and
the alternative of casting the numeric constant to integer and applying the integer + integer operator. 

Lacking any knowledge of which choice to prefer, it would give up and declare the query ambiguous. 

The fact that only one of the two casts is implicit is the way in which we teach the parser to prefer resolution of
a mixed numeric-and-integer expression as numeric; 

there is no built-in knowledge about that. 

因此,建议谨慎使用AS IMPLICIT。建议使用AS IMPLICIT的CAST应该是非失真转换转换,例如从INT转换为TEXT,或者int转换为numeric。

而失真转换,不建议使用as implicit,例如numeric转换为int。

It is wise to be conservative about marking casts as implicit. 

An overabundance of implicit casting paths can cause PostgreSQL to choose surprising interpretations of commands,
or to be unable to resolve commands at all because there are multiple possible interpretations. 

A good rule of thumb is to make a cast implicitly invokable only for information-preserving
transformations between types in the same general type category. 

For example, the cast from int2 to int4 can reasonably be implicit,
but the cast from float8 to int4 should probably be assignment-only. 

Cross-type-category casts, such as text to int4, are best made explicit-only. 

注意事项 + 例子

不能嵌套转换。例子

1、将text转换为date

错误方法

create or replace function text_to_date(text) returns date as $$
 select cast($1 as date);
$$ language sql strict; 

create cast (text as date) with function text_to_date(text) as implicit; 

嵌套转换后出现死循环

postgres=# select text '2017-01-01' + 1;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 2048kB), after ensuring the platform's stack depth limit is adequate.
CONTEXT: SQL function "text_to_date" during startup
SQL function "text_to_date" statement 1
SQL function "text_to_date" statement 1
SQL function "text_to_date" statement 1
...... 

正确方法

create or replace function text_to_date(text) returns date as $$
 select to_date($1,'yyyy-mm-dd');
$$ language sql strict; 

create cast (text as date) with function text_to_date(text) as implicit;
postgres=# select text '2017-01-01' + 1;
 ?column?
------------
 2017-01-02
(1 row) 

我们还可以直接使用IO函数来转换:

postgres=# create cast (text as date) with inout as implicit;
CREATE CAST

postgres=# select text '2017-01-01' + 1;
 ?column?
------------
 2017-01-02
(1 row)

补充:PostgreSQL 整型int与布尔boolean的自动转换设置(含自定义cast与cast规则介绍)

背景

在使用数据库时,经常会遇到一些因为客户端输入的类型与数据库定义的类型不匹配导致的错误问题。

例如数据库定义的是布尔类型,而输入的是整型:

postgres=# create table cas_test(id int, c1 boolean);
CREATE TABLE 

postgres=# \set VERBOSITY verbose
postgres=# insert into cas_test values (1, int '1');
ERROR: 42804: column "c1" is of type boolean but expression is of type integer
LINE 1: insert into cas_test values (1, int '1');
           ^
HINT: You will need to rewrite or cast the expression.
LOCATION: transformAssignedExpr, parse_target.c:591 

又或者数据库定义的是时间,用户输入的是字符串:

postgres=# create table tbl123(id int, crt_time timestamp);
CREATE TABLE 

postgres=# insert into tbl123 values (1, text '2017-01-01 10:00:00');
ERROR: column "crt_time" is of type timestamp without time zone but expression is of type text
LINE 1: insert into tbl123 values (1, text '2017-01-01 10:00:00');
           ^
HINT: You will need to rewrite or cast the expression. 

从错误提示来看,数据库已经很清晰的告诉你为什么了。那么怎么让数据库自动转换呢?

PostgreSQL有一个语法,支持数据类型的转换(赋值、参数、表达式 等位置的自动转换)。

postgres=# \h create cast
Command:  CREATE CAST
Description: define a new cast
Syntax:
CREATE CAST (source_type AS target_type)
 WITH FUNCTION function_name [ (argument_type [, ...]) ]
 [ AS ASSIGNMENT | AS IMPLICIT ] 

CREATE CAST (source_type AS target_type)
 WITHOUT FUNCTION
 [ AS ASSIGNMENT | AS IMPLICIT ] 

CREATE CAST (source_type AS target_type)
 WITH INOUT
 [ AS ASSIGNMENT | AS IMPLICIT ] 

数据库内置了很多转换法则:

postgres=# \dC
            List of casts
   Source type   |   Target type   |   Function   | Implicit?
-----------------------------+-----------------------------+---------------------------+---------------
 abstime      | date      | date      | in assignment
 abstime      | integer      | (binary coercible)  | no
 abstime      | timestamp without time zone | timestamp     | yes
 ........
 integer      | boolean      | bool      | no 

类型的自动转换实际上也是有一定的规则的,例如 赋值、参数 算是两种规则。具体含义见如下文档:

PostgreSQL 自定义自动类型转换(CAST)

我们看到整型转布尔是有内置的转换规则的,那么为什么没有自动转呢?

postgres=# \dC
            List of casts
   Source type   |   Target type   |   Function   | Implicit?
-----------------------------+-----------------------------+---------------------------+---------------
 integer      | boolean      | bool      | no 

和自动转换的规则有关,no表示不会自动转换,只有当我们强制指定转换时,才会触发转换的动作:

postgres=# select cast ((int '1') as boolean);
 bool
------
 t
(1 row) 

pg_cast里面的context转换为可读的内容(e表示no, a表示assignment, 否则表示implicit)

如果让数据库赋值时自动将字符串转换为时间,自动将整型转换为布尔

1、如果数据库已经内置了转换规则,那么可以通过更新系统表的方式,修改自动转换规则。

例如,将这个INT转BOOLEAN的规则,修改为assignment的规则。

postgres=# update pg_cast set castcontext='a' where castsource ='integer'::regtype and casttarget='boolean'::regtype;
UPDATE 1 

修改后,我们再查看这个转换规则,就变成这样了

\dC
            List of casts
   Source type   |   Target type   |   Function   | Implicit?
-----------------------------+-----------------------------+---------------------------+---------------
 integer      | boolean      | bool      | in assignment 

现在你可以将int自动写入为BOOLEAN了。

postgres=# create table cas_test(id int, c1 boolean);
CREATE TABLE
postgres=# insert into cas_test values (1, int '1');
INSERT 0 1 

2、如果系统中没有两种类型转换的CAST规则,那么我们需要自定义一个。

例如

postgres=# create cast (text as timestamp) with inout as ASSIGNMENT;
CREATE CAST 

            List of casts
   Source type   |   Target type   |   Function   | Implicit?
-----------------------------+-----------------------------+---------------------------+---------------
 text      | timestamp without time zone | (binary coercible)  | in assignment 

这样就可以自动将TEXT转换为TIMESTAMP了。

postgres=# insert into tbl123 values (1, text '2017-01-01 10:00:00');
INSERT 0 1
postgres=# select * from tbl123;
 id |  crt_time
----+---------------------
 1 | 2017-01-01 10:00:00
(1 row) 

删掉这个转换,就会报错。

postgres=# drop cast (text as timestamp);
DROP CAST
postgres=# insert into tbl123 values (1, text '2017-01-01 10:00:00');
ERROR: column "crt_time" is of type timestamp without time zone but expression is of type text
LINE 1: insert into tbl123 values (1, text '2017-01-01 10:00:00');
           ^
HINT: You will need to rewrite or cast the expression. 

3、如果没有内置的转换函数,我们可能需要自定义转换函数来支持这种转换。

例子

自定义一个函数,用于输入TEXT,返回TIMESTAMPTZ

postgres=# create or replace function cast_text_to_timestamp(text) returns timestamptz as $$
 select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss');
$$ language sql strict ;
CREATE FUNCTION 

建立规则

postgres=# create cast (text as timestamptz) with function cast_text_to_timestamp as ASSIGNMENT;
CREATE CAST 

postgres=# \dC
            List of casts
   Source type   |   Target type   |   Function   | Implicit?
-----------------------------+-----------------------------+---------------------------+---------------
 text      | timestamp with time zone | cast_text_to_timestamp | in assignment 

现在,输入TEXT,就可以自定转换为timestamptz了。

postgres=# create table tbl1234(id int, crt_time timestamptz);
CREATE TABLE
postgres=# insert into tbl1234 values (1, text '2017-01-01 10:10:10');
INSERT 0 1 

当然,这些类型实际上内部都有内部的存储格式,大多数时候,如果存储格式通用,就可以直接使用INOUT来转换,不需要写转换函数。

仅仅当两种类型在数据库的内部存储格式不一样的时候,需要显示的写函数来转换。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • 解决sqoop从postgresql拉数据,报错TCP/IP连接的问题

    问题: sqoop从postgresql拉数据,在执行到mapreduce时报错Connection refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections 问题定位过程: 1.postgresql 5432端口已开放,执行任务的节点能telnet通,并且netcat测试通过 2.sqoop list-tables命令可正常执行,sq

  • 解决postgreSql远程连接数据库超时的问题

    首先在cmd中ping 这个ip如果发现可以ping通就可以考虑是 远程数据库开启了防火墙.或者数据库设置该ip不能访问. 防火墙问题:可以考虑直接关闭防火墙,或者设置防火墙开放5432端口 然后到postgresql安装目录下data中修改pg_hba.conf文件,配置用户的访问权限,拉到底部 host all all 127.0.0.1/32 trust host all all 192.168.1.0/24 md5 #表示允许网段192.168.1.0上的所有主机使用所有合法的数据库用户

  • PostgreSQL 允许远程访问设置的操作

    postgres远程连接方式配置 配置pg_hba.conf文件 目录C:\Program Files\PostgreSQL\9.5\data (QXY)主机 [postgres@qxy data]$ pwd /spark/pgsql/data [postgres@qxy data]$ cat pg_hba.conf # TYPE DATABASE USER ADDRESS METHOD # "local" is for Unix domain socket connections o

  • PostgreSQL 设置允许访问IP的操作

    PostgreSQL安装后默认只能localhost:5432访问 检验方法: curl localhost:5432 # 访问成功提示 curl: (52) Empty reply from server curl 127.0.0.1:5432 # 访问不成功提示 curl: (7) Failed to connect to 172.17.201.227 port 5432: Connection refused 修改pg_hba.conf pg_hba.conf和postgresql.con

  • navicat无法连接postgreSQL-11的解决方案

    1. 通过find / -name postgresql.conf 和 find / -name pg_hba.conf 找到这两个文件 2. 设置外网访问: 1)修改配置文件 postgresql.conf listen_addresses = '*' 2)修改pg_hba.conf 在原来的host下面新加一行 # IPv4 local connections: host all all 127.0.0.1/32 trust host all all 0.0.0.0/0 password 3

  • postgreSQL 数字与字符串类型转换操作

    数字转字符串 select cast(123 as VARCHAR); 字符串转数字 select cast('123' as INTEGER); 以此类推,应该也可以转换为其他数据类型. 补充:pgSql, mySql中字符串转化为数字 pgSql 语法 to_number(text, text) 例子 select to_number(trim(both 'ibs' from classname), '999999') as cn from bbs order by cn /*trim(bo

  • PostgreSQL 自定义自动类型转换操作(CAST)

    背景 PostgreSQL是一个强类型数据库,因此你输入的变量.常量是什么类型,是强绑定的,例如 在调用操作符时,需要通过操作符边上的数据类型,选择对应的操作符. 在调用函数时,需要根据输入的类型,选择对应的函数. 如果类型不匹配,就会报操作符不存在,或者函数不存在的错误. postgres=# select '1' + '1'; ERROR: operator is not unique: unknown + unknown LINE 1: select '1' + '1'; ^ HINT:

  • java SpringBoot自定义注解,及自定义解析器实现对象自动注入操作

    # java-SpringBoot自定义参数解析器实现对象自动注入 解析器逻辑流程图表 后台解析注解的解析器 首先,我在java后台编写了一个解析器,代码如下 import com.ruoyi.framework.interceptor.annotation.LoginUser; import com.ruoyi.project.WebMoudle.WebUser.domain.WebUser; import com.ruoyi.project.WebMoudle.WebUser.service

  • SpringBoot自定义对象参数实现自动类型转换与格式化

    目录 序章 一.实体类 Bean 二.前端表单index.html 三.Controller 类 四.运行结果截图 序章 问题提出一: 当我们用表单获取一个 Person 对象的所有属性值时, SpringBoot 是否可以直接根据这些属性值将其转换为 Person 对象 回答: 当然可以,SpringBoot 通过自定义对象参数,可以实现自动类型转换与格式化,并可以级联封装(一个对象拥有另一个对象作为属性时,也可以封装). 一.实体类 Bean person类 注: 构造方法一定要写全,无参数

  • 有趣的JavaScript隐式类型转换操作实例分析

    本文实例讲述了JavaScript隐式类型转换操作.分享给大家供大家参考,具体如下: JavaScript的数据类型是非常弱的(不然不会叫它做弱类型语言了)!在使用算术运算符时,运算符两边的数据类型可以是任意的,比如,一个字符串可以和数字相加.之所以不同的数据类型之间可以做运算,是因为JavaScript引擎在运算之前会悄悄的把他们进行了隐式类型转换的,如下是数值类型和布尔类型的相加: 3 + true; // 4 结果是一个数值型!如果是在C或者Java环境的话,上面的运算肯定会因为运算符两边

  • 关于Kotlin的自动类型转换详解

    前言 Kotlin 1.4 正式版在好早以前就已经发布了.关于那些"看得见"的新特性,比如SAM转换.参数列表末尾的可选逗号什么的,已经有无数文章介绍过了.所以本文打算介绍一些可能是鲜为人知的.Kotlin 官方团队偷偷塞进 1.4 的新特性. 不过单独讲这些东西会显得文章太过单薄,于是我打算把其他相似的东西拉一起凑凑字数. 本文使用的 Kotlin 版本为 Kotlin 1.4. 本文要讲的东西 看题目就知道了,Kotlin 里自动类型转换(automatic type conver

  • Golang 语言极简类型转换库cast的使用详解

    目录 01 介绍 02 转换为字符串类型 03 总结 01 介绍 在 Golang 语言项目开发中,因为 Golang 语言是强类型,所以经常会使用到类型转换.本文我们介绍类型转换三方库 - github.com/spf13/cast ,它是一个极简类型转换的三方库,通过它提供的函数,可以方便我们进行类型转换,极大提升我们的开发效率. 并且, cast 按照一定规则,自动执行正确的操作,例如,当我们使用  cast.ToInt() 将字符串转换为整型时,只有参数是 int 的字符串时,例如  "

  • golang类型转换组件Cast的使用详解

    开源地址 https://github.com/spf13/cast Cast是什么? Cast是一个库,以一致和简单的方式在不同的go类型之间转换. Cast提供了简单的函数,可以轻松地将数字转换为字符串,将接口转换为bool类型等等.当一个明显的转换是可能的时,Cast会智能地执行这一操作.它不会试图猜测你的意思,例如,你只能将一个字符串转换为int的字符串表示形式,例如"8".Cast是为Hugo开发的,Hugo是一个使用YAML.TOML或JSON作为元数据的网站引擎. 为什么

  • 使用JPA自定义VO类型转换(EntityUtils工具类)

    目录 JPA自定义VO类型转换(EntityUtils工具类) dto,vo,po,bo等实体转换工具类 下面宣布这次的主角:dozer JPA自定义VO类型转换(EntityUtils工具类) 在JPA查询中,如果需要返回自定义的类,可以使用EntityUtils工具类,该类源码: import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor; import java.

  • 利用SpringDataJPA开启审计功能,自动保存操作人操作时间

    目录 1 相关注解 实现自动记录上述信息主要有5个注解 2 实现过程 2.1 依赖引用 2.2 实体类标记审计属性 2.3 审计自定义操作 2.4 应用开启审计功能 2.5 实体操作 有些业务数据对数据的创建.最后更新时间以及创建.最后操作人进行记录.如果使用Spring Data Jpa做数据新增或更新,可实现自动保存这些信息而不需要显示设置对应字段的值,可通过以下步骤进行配置. 1 相关注解 实现自动记录上述信息主要有5个注解 @EnableJpaAuditing:审计功能开关 @Creat

  • Spring中自定义数据类型转换的方法详解

    目录 类型转换服务 实现Converter接口 实现ConverterFactory接口 实现GenericConverter接口 环境:Spring5.3.12.RELEASE. Spring 3引入了一个core.onvert包,提供一个通用类型转换系统.系统定义了一个SPI来实现类型转换逻辑,以及一个API来在运行时执行类型转换.在Spring容器中,可以使用这个系统作为PropertyEditor实现的替代,将外部化的bean属性值字符串转换为所需的属性类型.还可以在应用程序中需要类型转

随机推荐