Pay attention: Oracle INTEGER is NUMBER(p) not INT4 in PostgreSQL

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介:
今天一位朋友问我Oracle转换到PostgreSQL时,Oracle的INT应该转换为PostgreSQL的什么类型?
差点被integer这个词迷惑,其实在Oracle中,integer使用NUMBER来存储的,只是不存储小数。
例如:
SQL> set numwidth 50
SQL> create table test(id int);

Table created.

SQL> insert into test values (9999999999999999999);

1 row created.

SQL> select * from test;

                                                ID
--------------------------------------------------
                               9999999999999999999

在sqlplus客户端中,数字长度显示默认为10,超出的话会用科学方法表示,所以不要误以为这是精度问题哦。
SQL> set numwidth 10
SQL> select * from test;
        ID
----------
1.0000E+19

精度实际上是NUMBER(38)。超过可能遇到BUG,虽然可以存储进去。例如以下,40个9显示正常,但是41个9时进位了,已经精度不准确。
SQL> insert into test values (9999999999999999999999999999999999999999);
SQL> insert into test values (99999999999999999999999999999999999999999);
SQL> select * from test;
                                                ID
--------------------------------------------------
         9999999999999999999999999999999999999999
        100000000000000000000000000000000000000000

使用NUMERIC表现一样:
SQL> drop table test;
Table dropped.
SQL> create table test(id numeric);
Table created.
SQL> insert into test values (9999999999999999999999999999999999999999);
SQL> insert into test values (99999999999999999999999999999999999999999);
SQL> select * from test;
                                                ID
--------------------------------------------------
          9999999999999999999999999999999999999999
        100000000000000000000000000000000000000000


在PostgreSQL中,我们要使用对应的numeric类型来代替Oracle的int类型,并且非常完美,超出40位没有问题。
postgres=# select 9999999999999999999999999999999999999999::numeric;
                 numeric                  
------------------------------------------
 9999999999999999999999999999999999999999
(1 row)

postgres=# select 99999999999999999999999999999999999999999::numeric;
                  numeric                  
-------------------------------------------
 99999999999999999999999999999999999999999
(1 row)

postgres=# select 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::numeric;
                                             numeric                                             
-------------------------------------------------------------------------------------------------
 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
(1 row)

postgres=# select 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::numeric;
                                                                                                             numeric                
                                                                                             
------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------
 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
(1 row)

postgres=# select 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::numeric;
                                                                                                                                    
                                            numeric                                                                                 
                                                                                               
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
(1 row)

postgres=# select 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998888888888888888888888888888888888888888888888888888888888888888888888888888::numeric;
                                                                                                                                    
                                                                                                                                 num
eric                                                                                                                                
                                                                                                                                    
  
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
--
 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
999999999999999999999999999999999999999999999999999999999888888888888888888888888888888888888888888888888888888888888888888888888888
8
(1 row)


postgres=# create table test(id numeric);
CREATE TABLE
postgres=# insert into test values (9999999999999999999999999999999999999999);
INSERT 0 1
postgres=# insert into test values (99999999999999999999999999999999999999999);
INSERT 0 1
postgres=# select * from test;
                    id                     
-------------------------------------------
  9999999999999999999999999999999999999999
 99999999999999999999999999999999999999999
(2 rows)

postgres=# insert into test values (9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999991111111111111111111111111111111111111111111111111111111111111111111);
INSERT 0 1
postgres=# select * from test;
                                                                                id                                                  
                              
------------------------------------------------------------------------------------------------------------------------------------
------------------------------
                                                                                                                         99999999999
99999999999999999999999999999
                                                                                                                        999999999999
99999999999999999999999999999
 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999911111111111111111111111111111111111111
11111111111111111111111111111
(3 rows)

那么PostgreSQL的numeric精度有多大呢?
src/include/utils/numeric.h
/*-------------------------------------------------------------------------
 *
 * numeric.h
 *        Definitions for the exact numeric data type of Postgres
 *
 * Original coding 1998, Jan Wieck.  Heavily revised 2003, Tom Lane.
 *
 * Copyright (c) 1998-2010, PostgreSQL Global Development Group
 *
 * $PostgreSQL: pgsql/src/include/utils/numeric.h,v 1.29 2010/01/02 16:58:10 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */

/*
 * The Numeric data type stored in the database
 *
 * NOTE: by convention, values in the packed form have been stripped of
 * all leading and trailing zero digits (where a "digit" is of base NBASE).
 * In particular, if the value is zero, there will be no digits at all!
 * The weight is arbitrary in that case, but we normally set it to zero.
 */
typedef struct NumericData
{
        int32           vl_len_;                /* varlena header (do not touch directly!) */
        uint16          n_sign_dscale;  /* Sign + display scale */
        int16           n_weight;               /* Weight of 1st digit  */
        char            n_data[1];              /* Digits (really array of NumericDigit) */
} NumericData;

typedef NumericData *Numeric;

/*
 * Hardcoded precision limit - arbitrary, but must be small enough that
 * dscale values will fit in 14 bits.
 */
#define NUMERIC_MAX_PRECISION           1000


如果你要限制numeric的精度,那么最大允许1000的长度限制。
但是如果你不限制,允许多大的数字呢?
数字总长度不能超过uint16+uint16+2^14,即131072+16384,其中131072为整数部分,16384为小数点以及小数部分。
postgres=# select 2^16 + 2^16;
 ?column? 
----------
   131072
(1 row)

postgres=# select repeat('9',131073)::numeric;
ERROR:  22003: value overflows numeric format
LOCATION:  make_result, numeric.c:4202

postgres=# select repeat('9',131072)::numeric;
正常返回

postgres=# select (repeat('9',131072)||'.'||repeat('9',16384))::numeric;
ERROR:  22003: value overflows numeric format
LOCATION:  make_result, numeric.c:4202

postgres=# select (repeat('9',131072)||'.'||repeat('9',16383))::numeric;
正常返回

参考, src/backend/utils/adt/numeric.c
其实有两种格式,一种SHORT,一种LONG。
/*
 * The Numeric type as stored on disk.
 *
 * If the high bits of the first word of a NumericChoice (n_header, or
 * n_short.n_header, or n_long.n_sign_dscale) are NUMERIC_SHORT, then the
 * numeric follows the NumericShort format; if they are NUMERIC_POS or
 * NUMERIC_NEG, it follows the NumericLong format.  If they are NUMERIC_NAN,
 * it is a NaN.  We currently always store a NaN using just two bytes (i.e.
 * only n_header), but previous releases used only the NumericLong format,
 * so we might find 4-byte NaNs on disk if a database has been migrated using
 * pg_upgrade.  In either case, when the high bits indicate a NaN, the
 * remaining bits are never examined.  Currently, we always initialize these
 * to zero, but it might be possible to use them for some other purpose in
 * the future.
 *
 * In the NumericShort format, the remaining 14 bits of the header word
 * (n_short.n_header) are allocated as follows: 1 for sign (positive or
 * negative), 6 for dynamic scale, and 7 for weight.  In practice, most
 * commonly-encountered values can be represented this way.
 *
 * In the NumericLong format, the remaining 14 bits of the header word
 * (n_long.n_sign_dscale) represent the display scale; and the weight is
 * stored separately in n_weight.
 *
 * NOTE: by convention, values in the packed form have been stripped of
 * all leading and trailing zero digits (where a "digit" is of base NBASE).
 * In particular, if the value is zero, there will be no digits at all!
 * The weight is arbitrary in that case, but we normally set it to zero.
 */
struct NumericShort
{
        uint16          n_header;               /* Sign + display scale + weight */
        NumericDigit n_data[1];         /* Digits */
};

struct NumericLong
{
        uint16          n_sign_dscale;  /* Sign + display scale */
        int16           n_weight;               /* Weight of 1st digit  */
        NumericDigit n_data[1];         /* Digits */
};

union NumericChoice
{
        uint16          n_header;               /* Header word */
        struct NumericLong n_long;      /* Long form (4-byte header) */
        struct NumericShort n_short;    /* Short form (2-byte header) */
};

struct NumericData
{
        int32           vl_len_;                /* varlena header (do not touch directly!) */
        union NumericChoice choice; /* choice of format */
};


/*
 * make_result() -
 *
 *      Create the packed db numeric format in palloc()'d memory from
 *      a variable.
 */
static Numeric
make_result(NumericVar *var)
{
        Numeric         result;
        NumericDigit *digits = var->digits;
        int                     weight = var->weight;
        int                     sign = var->sign;
        int                     n;
        Size            len;

        if (sign == NUMERIC_NAN)
        {
                result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT);

                SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT);
                result->choice.n_header = NUMERIC_NAN;
                /* the header word is all we need */

                dump_numeric("make_result()", result);
                return result;
        }

        n = var->ndigits;

        /* truncate leading zeroes */
        while (n > 0 && *digits == 0)
        {
                digits++;
                weight--;
                n--;
        }
        /* truncate trailing zeroes */
        while (n > 0 && digits[n - 1] == 0)
                n--;

        /* If zero result, force to weight=0 and positive sign */
        if (n == 0)
        {
                weight = 0;
                sign = NUMERIC_POS;
        }

        /* Build the result */
        if (NUMERIC_CAN_BE_SHORT(var->dscale, weight))
        {
                len = NUMERIC_HDRSZ_SHORT + n * sizeof(NumericDigit);
                result = (Numeric) palloc(len);
                SET_VARSIZE(result, len);
                result->choice.n_short.n_header =
                        (sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK)
                         : NUMERIC_SHORT)
                        | (var->dscale << NUMERIC_SHORT_DSCALE_SHIFT)
                        | (weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0)
                        | (weight & NUMERIC_SHORT_WEIGHT_MASK);
        }
        else
        {
                len = NUMERIC_HDRSZ + n * sizeof(NumericDigit);
                result = (Numeric) palloc(len);
                SET_VARSIZE(result, len);
                result->choice.n_long.n_sign_dscale =
                        sign | (var->dscale & NUMERIC_DSCALE_MASK);
                result->choice.n_long.n_weight = weight;
        }

        memcpy(NUMERIC_DIGITS(result), digits, n * sizeof(NumericDigit));
        Assert(NUMERIC_NDIGITS(result) == n);

        /* Check for overflow of int16 fields */
        if (NUMERIC_WEIGHT(result) != weight ||
                NUMERIC_DSCALE(result) != var->dscale)
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("value overflows numeric format")));

        dump_numeric("make_result()", result);
        return result;
}


numeric variable user-specified precision, exact no limit

[参考]
3.  src/include/utils/numeric.h
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
29天前
|
Oracle 关系型数据库 分布式数据库
PolarDB常见问题之PolarDB(Oracle兼容版) 执行命令报错如何解决
PolarDB是阿里云推出的下一代关系型数据库,具有高性能、高可用性和弹性伸缩能力,适用于大规模数据处理场景。本汇总囊括了PolarDB使用中用户可能遭遇的一系列常见问题及解答,旨在为数据库管理员和开发者提供全面的问题指导,确保数据库平稳运行和优化使用体验。
|
1月前
|
关系型数据库 分布式数据库 数据库
PolarDB PostgreSQL版:Oracle兼容的高性能数据库
PolarDB PostgreSQL版是一款高性能的数据库,具有与Oracle兼容的特性。它采用了分布式架构,可以轻松处理大量的数据,同时还支持多种数据类型和函数,具有高可用性和可扩展性。它还提供了丰富的管理工具和性能优化功能,为企业提供了可靠的数据存储和处理解决方案。PolarDB PostgreSQL版在数据库领域具有很高的竞争力,可以满足各种企业的需求。
|
7月前
|
Oracle 关系型数据库 数据库
PostgreSQL和Oracle两种数据库有啥区别?如何选择?
PostgreSQL和Oracle两种数据库有啥区别?如何选择?
201 0
|
8月前
|
SQL Oracle 关系型数据库
物化视图(Oracle与PostgreSQL对比)
物化视图(Oracle与PostgreSQL对比)
|
8月前
|
SQL Oracle 关系型数据库
PostgreSQL技术大讲堂 - 第27讲:Oracle-FDW部署
从零开始学PostgreSQL,PG技术大讲堂 - 第27讲:Oracle-FDW部署
167 2
|
4月前
|
SQL Oracle 关系型数据库
Oracle,Postgresql等数据库使用
Oracle,Postgresql等数据库简单使用
133 0
Oracle,Postgresql等数据库使用
|
7月前
|
Oracle 关系型数据库 分布式数据库
如何从Oracle迁移到PolarDB(ADAM)(二)
如何从Oracle迁移到PolarDB(ADAM)(二)
128 0
|
7月前
|
SQL Oracle 关系型数据库
Polar DB-O (兼容 Oracle 语法版本)和Polar DB PostgreSQL 版本概述(二)
Polar DB-O (兼容 Oracle 语法版本)和Polar DB PostgreSQL 版本概述(二)
692 0
|
11月前
|
SQL 安全 Oracle
MySQL&SQL server&Oracle&Access&PostgreSQL数据库sql注入详解
MySQL&SQL server&Oracle&Access&PostgreSQL数据库sql注入详解
343 0
|
SQL Oracle 网络协议
【.NET 6】使用EF Core 访问Oracle+Mysql+PostgreSQL并进行简单增改操作与性能比较
唠嗑一下。都在说去O或者开源,但是对于数据库选型来说,很多人却存在着误区。例如,去O,狭义上讲,是去Oracle数据库。但是从广义上来说,是去Oracle公司产品或者具有漂亮国垄断地位和需要商业授权的数据库产品。
323 0
【.NET 6】使用EF Core 访问Oracle+Mysql+PostgreSQL并进行简单增改操作与性能比较