ITPub博客

首页 > 数据库 > 数据库开发技术 > Sybase ASE中的char(N)的坑以及与PostgreSQL的对比

Sybase ASE中的char(N)的坑以及与PostgreSQL的对比

原创 数据库开发技术 作者:数据库杂记 时间:2023-03-10 05:50:47 0 删除 编辑

1背景

昨天,一朋友向我咨询Sybase ASE中定长字符串类型的行为,说他们的客户反映,同样的char类型的数据,通过jdbc来查,Sybase库不会带空格,而PostgreSQL会带。是不是这样的?他是PostgreSQL的专业大拿,但因为他手头没有现成的Sybase ASE环境,刚好我手上有,便于一试。

2 分析

通常遇到这种情况,我会想,一定有什么地方使用起来有些特殊。不然不太可能会出现截然不同的结果。在我多年的映象中,ASE中的char(N)就是一种定长类型。没有什么特殊处理。

PostgreSQL里头的定长字符串比较行为相当正常, 完全符合预期:

如果用另一个拼接式的SQL,看一下:

postgres=# select '[' || 'Beijing  '::char(12) || ']';
 ?column?
-----------
 [Beijing]
(1 row)

看起来,它是把末尾的所谓的空格给消掉了。

看来边的示例:

create table t(id char(12));
insert into t values(' Beijing ');

这里就是一张简单的表,(注:没有NULL 声明,默认为NULL, 因为数据库选项将此开关"allow nulls by default"置为ON, 此开关如果不开,则默认为NOT NULL,很我初学初用的人易犯错)。插入首尾都带空格的字符串' Beijng ',  我们select一下,看看结果。为了简化问题,也不想写什么code,直接用 DBEaver直连,不带任何附加参数,发现select出来的值,空格都没有被trim.

select * from t;

这里还特意做一个二进制的转换,再次确认:

select convert(binary, id) from t;
select convert(binary, 'Beijing') from t;

将结果,以及去掉首尾字母的'Beijing'的binary值出来,对比分别是下边这样子的:

从这一看,前边的20空格字符确实还在。后边一大堆的0x00是什么原因?于是又补了一个查询:

select convert(binary(12), 'Beijing  ') from t;

其结果是末尾都是'0x20'空格。而且它与下这的两句结果是相同的:

select convert(binary(12), convert(char(9), 'Beijing  ')) from t;
select convert(binary(12), convert(varchar(9), 'Beijing  ')) from t;

看起来,直接SELECT出来的结果并没有截断。而经过binary转换以后,末尾也都变成00了。但是验证是否符合预期也直观:

直接使用: 

select * from t where id = ' Beijing'
最终还是能查出符合预期的结果:

但是我还是不放心。两端用'[', 括一下试试:

select '[' + id + ']' as id, '[' + convert(char(12) null, ' Beijing ') + ']' as v from t;
select '[' + id + ']' as id from t;
结果是这样的:

妥妥的被截掉了。那就是说,在与其它字符一起拼接的时候,它是真的被截掉了。

3 进一步比对

重新查了一下文档,发现ASE有一些关于strip的连接属性:

https://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc39001.1600/doc/html/har1355981355759.html (Sybase ASE 16.0 JConnect相关连接属性)里有一段:

STRIP_BLANKS: 
Forces the server to remove the preceding and trailing blanks in a string value before storing it in the table.
Valid values are:
false – string values sent by the client are stored as is.
true – preceding and trailing blanks in a string value are removed before storing it in the table.
Default value is false.
This property is static.

就是在连接串末尾加上:&STRIP_BLANKS=true, 结果发现和上边的一样,没什么不同(这就有点儿不太正常了)

4 换一种方式对比

create table spaces (cnot char(5) not null, 
    cnull char(5) null, 
    vnot varchar(5) not null, 
    vnull varchar(5) null, 
    explanation varchar(25) not null) ;
把几种情形都考虑进去,看看有什么不同?
insert spaces values ('a', 'b', 'c', 'd', 'pads char-not-null only') ;
insert spaces values ('1    ', '2    ', '3    ', '4    ', 'truncates trailing blanks');
insert spaces values ('    e', '    f', '    g', '    h', 'leading blanks, no change');
insert spaces values ('   w ', '   x ', '   y ', '    z ', 'truncates trailing blanks');
insert spaces values ('', '', '', '', 'empty string equals space') ;

select '[' + cnot + ']', 
       '[' + cnull + ']', 
       '[' + vnot + ']', 
       '[' + vnull + ']', 
    explanation from spaces 

结果如下:

解释起来就是:

  • 如果是Nullable的char(N),则会把末尾的空格截掉(当然,这是与其它字符拼接时的行为)

  • 如果是Not Null的char(N),则不会截掉空格,无论是否与其它字符拼接。

  • 如果是varchar(N),则无论是否是Nullable,都会被截掉空格(无论是否拼接)


5. 直接用jdbc的代码进行验证以及结果

表及数据准备:

create table t2(col1 char(12) null, col2 char(12) not null, col3 varchar(12) null, col4 varchar(12) not null);
insert into t2 values('Beijing ', 'Beijing ', 'Nanjing ', 'Nanjing ');

5.1 ASE的查询:

select col1, col2, '[' + col1 + ']' as newcol1, '[' + col2 + ']' as newcol2,
       col3, col4, '[' + col3 + ']' as newcol3, '[' + col4 + ']' as newcol4
from t2;

直接查询的java代码:

        try (Connection conn = DriverManager.getConnection("jdbc:sybase:Tds:host:5000?ServiceName=sean", "username",
                "password")) {
            try (Statement stmt = conn.createStatement();
                    ResultSet rset = stmt.executeQuery(
                            "select col1, col2, '[' + col1 + ']' as newcol1, '[' + col2 + ']' as newcol2, col3, col4, '[' + col3 + ']' as\n"
                                    + " newcol3, '[' + col4 + ']' as newcol4 from t2")) {
                if (rset.next()) {
                    System.out.println("col1: (" + rset.getString(1) + ")");
                    System.out.println("col2: (" + rset.getString(2) + ")");
                    System.out.println("ncol1: (" + rset.getString(3) + ")");
                    System.out.println("ncol2: (" + rset.getString(4) + ")");
                    System.out.println("col3: (" + rset.getString(5) + ")");
                    System.out.println("col4: (" + rset.getString(6) + ")");
                    System.out.println("ncol3: (" + rset.getString(7) + ")");
                    System.out.println("ncol4: (" + rset.getString(8) + ")");
                }
            }
        }

有趣的结果如下:

col1: (Beijing     )
col2: (Beijing     )
ncol1: ([Beijing])
ncol2: ([Beijing     ])
col3: (Nanjing)
col4: (Nanjing)
ncol3: ([Nanjing])
ncol4: ([Nanjing])
char(12) not null永远不截断。char(12) null, 拼接后取值,会截断。取值
以后再拼接,则保留空格(见第1行结果和第3行结果)
5.2 PostgreSQL的查询:
select col1, col2, '[' + col1 + ']' as newcol1, '[' + col2 + ']' as newcol2,
       col3, col4, '[' + col3 + ']' as newcol3, '[' + col4 + ']' as newcol4
from t2;

它对应的java代码:

        try (Connection conn = DriverManager.getConnection("jdbc:postgresql://127.0.0.1:5555/mydb", "username",
                "password")) {
            try (Statement stmt = conn.createStatement();
                    ResultSet rset = stmt.executeQuery(
                            "select col1, col2, '[' || col1 || ']' as newcol1, '[' || col2 || ']' as newcol2,\n"
                                    + "       col3, col4, '[' || col3 || ']' as newcol3, '[' || col4 || ']' as newcol4 \n"
                                    + "from t2")) {
                if (rset.next()) {
                    System.out.println("col1: (" + rset.getString(1) + ")");
                    System.out.println("col2: (" + rset.getString(2) + ")");
                    System.out.println("ncol1: (" + rset.getString(3) + ")");
                    System.out.println("ncol2: (" + rset.getString(4) + ")");
                    System.out.println("col3: (" + rset.getString(5) + ")");
                    System.out.println("col4: (" + rset.getString(6) + ")");
                    System.out.println("ncol3: (" + rset.getString(7) + ")");
                    System.out.println("ncol4: (" + rset.getString(8) + ")");
                }
            }
        }

结果如下:

col1: (Beijing     )
col2: (Beijing     )
ncol1: ([Beijing])
ncol2: ([Beijing])
col3: (Nanjing )
col4: (Nanjing )
ncol3: ([Nanjing ])
ncol4: ([Nanjing ])

可以看到,char(N) NULL与NOT NULL,行为一致。都是直接取值时有空格,加了拼接时,空格截掉。

而varchar(N) NULL与NOT NULL,行为也一致,无论是直接取值,还是拼接后再取值,都留一个多余的字符(长度够宽的话)。如果你再往下深挖,可以把对应的byte打出来,它的10进制值是32,也就是空格字符。这个,我还真是好奇,PG jdbc是为何要多保留一个空格字符出来? 平时我还真没有注意到啊。呵呵。用下边的小片段做个留念:

postgres=# select '[' || 'Beijing '::char(12) || ']';
 ?column?
-----------
 [Beijing]
(1 row)

postgres=# select '[' || 'Beijing '::varchar(12) || ']';
  ?column?
------------
 [Beijing ]
(1 row)

6. 最终小结论:

在ASE中,如此看来,在char(12) null时,如果是SQL内拼接再取值,则会被截断。它如果是Not Null,则都不会被截断。这个跟PostgreSQL是不太一样的行为。PostgreSQL里,就算是Not Null, 拼接其它字符,再返回,也会被截掉。它的NULL与Not NULL的处理行为是一样的。

这样来看,只要不在SQL语句里头拼接,那么char(N) NULL或NOT NULL,两种数据库的行为应该是一样的。

[---END---]

原文作者:数据库杂记;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2019-04-22

  • 博文量
    276
  • 访问量
    160278