Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2945106
  • 博文数量: 199
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 4126
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-06 19:06
个人简介

半个PostgreSQL DBA,热衷于数据库相关的技术。我的ppt分享https://pan.baidu.com/s/1eRQsdAa https://github.com/chenhuajun https://chenhuajun.github.io

文章分类

全部博文(199)

文章存档

2020年(5)

2019年(1)

2018年(12)

2017年(23)

2016年(43)

2015年(51)

2014年(27)

2013年(21)

2011年(1)

2010年(4)

2009年(5)

2008年(6)

分类: Mysql/postgreSQL

2013-08-19 00:41:56

1. 概述

使用PostgreSQL一段时间后,就发现PG对数据类型处理上有个特点,基本上都以字符串形式进行数据的转换和传输。对于非文本型将其存储格式转化为字符串形式,或反过来将字符串解析为特定数据类型往往依赖上下文环境(locale,数据库参数等)所规定的格式。比如日期型的格式就由datestyle定义。

不同datestyle值的情况下,timestamptz型的输出形式不同:

  1. postgres=# set datestyle=SQL;
  2. SET
  3. postgres=# select now();
  4.              now
  5. -----------------------------

  6.  18/08/2013 22:56:33.457 HKT
  7. (1 行记录)


  8. postgres=# set datestyle=ISO;
  9. SET
  10. postgres=# select now();
  11.             now
  12. ----------------------------

  13.  2013-08-18 22:56:52.001+08
  14. (1 行记录)
这种表现形式不仅出现在psql的终端输入输出上,也作为jdbc等API和服务端交互时数据传输形式。为了能以简便的方式从字符串中提取出需要的值,各API往往在连接时就通过连接参数把输出格式确定下来。然而有些数据类型的存储和输入输出上的差异不太好处理,需要特别留意,比如下面几个。


2. money

和timestamptz类似,money的输入输出形式依赖于数据库的lc_monetary参数:

  1. postgres=# show lc_monetary;
  2.                      lc_monetary
  3. -----------------------------------------------------

  4.  Chinese (Simplified)_People's Republic of China.936
  5. (1 行记录)


  6. postgres=# select 1.1::money;
  7.  money
  8. --------

  9.  ¥1.10
  10. (1 行记录)


  11. postgres=# set lc_monetary='C';
  12. SET
  13. postgres=# select 1.1::money;
  14.  money
  15. -------

  16.  $1.10
  17. (1 行记录)

这里却有一个不同的地方,lc_monetary参数不仅仅影响了money的输入输出形式,还规定的money的语义(币种)。我们知道10美元和10人民币是不能划上等号的,而单独的money的存储格式里却不包含币种信息。所以money还不能算作真正的货币类型,这其实也限制了money在整个数据库里只能有一个统一的币种。
不仅如此,有些API在处理money时也犯了难,比如:pgjdbc将money映射为java的double,并假设货币为美元对服务端返回的money字符串进行解析。
这导致对服务端的lc_monetary参数值为美元以外的货币时,pgjdbc将会解析失败(*1)。npgsql将money映射为.net的decimal类型,并在解析时假设货币的小数位数一定是2位。这样在那些不符合这一假设的货币上就悲剧了,比如100日圆通过npgsql读出来就成了1日圆(*2)。

鉴于以上种种,建议避开money的使用,改用decimal 或者 numeric。

*1)回避方法为使用ResultSet.getString()直接获取字符串避开解析过程。
*2)开发版的npgsql已经解决了这个问题,方法为在建立连接时强制将lc_monetary参数值设为C,即美元。
*)在8.2及之前版本的文档里,曾声明货币数据类型是将要被废弃的一种数据类型,不推荐使用。但8.3有对其进行了改进,存储大小也有4字节扩大到8字节。手册追加了一些使用上的说明,并删掉了“将要被废弃”的描述。

3. timestamptz和timetz

和money类似的是timestamptz。timestamptz的存储格式里并不包括时区信息,在输出时使用timezone参数规定的时区格式化成带时区的字符串形式。但timetz的存储格式里是包含时区的,所以你会看到下面的现象。

timestamptz使用timezone参数规定的时区格式化输出形式:

  1. postgres=# show timezone;
  2.     TimeZone
  3. ----------------

  4.  Asia/Hong_Kong
  5. (1 行记录)

  6. postgres=# select '2013-08-18 11:39:11.153-04'::timestamptz;
  7.         timestamptz
  8. ----------------------------

  9.  2013-08-18 23:39:11.153+08
  10. (1 行记录)

timetz使用自带的时区格式化输出形式:

  1. postgres=# select '11:39:11.153-04'::timetz;
  2.      timetz
  3. -----------------

  4.  11:39:11.153-04
  5. (1 行记录)

比较timestamptz数据值时只比较是不是同一时刻:

  1. postgres=# select '2013-08-18 11:39:11.153-04'::timestamptz = '2013-08-18 23:39:
  2. 11.153+08'::timestamptz;
  3.  ?column?
  4. ----------

  5.  t
  6. (1 行记录)

比较timetz数据值时不仅比较时刻还比较时区:
  1. postgres=# select '11:39:11.153-04'::timetz = '23:39:11.153+08'::timetz;
  2.  ?column?
  3. ----------

  4.  f
  5. (1 行记录)

4. char(n)

char(n)也是一种需要留心的类型,它在运算时会截掉末尾的空格,输出时在末尾补上空格直到长度等于n。

  1. postgres=# select length('xx    '::char(30));
  2.  length
  3. --------

  4.       2
  5. (1 行记录)


  6. postgres=# select 'xx    '::char(30);
  7.    bpchar
  8. --------------------------------

  9.  xx
  10. (1 行记录)

 

阅读(10371) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~