Sybase空字符串陷阱:PG迁移必读
Barry Chen

使用过Sybase开发的同学都知道,Sybase的空字符串并不空,而是占一个字节的空格(' ')。直接上Sybase官方文档

Using the empty string
The empty string ("") or ('') is interpreted as a single blank in insert or assignment statements on varchar or univarchar data. In concatenation of varchar, char, nchar, nvarchar data, the empty string is interpreted as a single space; for following example is stored as “abc def”:
"abc" + "" + "def"


因为Sybase这个诡异的行为,导致在将Sybase数据库migrate到PostgreSQL时稍不留意,就会出问题。以下是我们项目组遇到的空字符串的案例汇总。

案例一:空字符串引发的自动化测试问题


首先看数据转换存在的问题。
Sybase中,当插入一个空字符串时,会被自动转换为一个空格(' ')。假设已存在表

CREATE TABLE hospital( code VARCHAR(10), name VARCHAR(255) )

执行:
INSERT INTO hospital VALUES('', 'HK')


Sybase会使用空格(' ')插入到表字段code中。
当使用自动化工具将hospital表数据迁移到PG时,自动化工具会认为Sybase中的空格' '是空字符串,于是往PG数据表插入一条code为空字符串的记录。
理论上,这是没有问题的。但我们在编写自动化比对脚本时,需要注意这一点,Sybase中的空格(' ')等于PG中的空字符串(''),否则就会产生误报。 

案例二:自动化转换工具对空字符串的处理问题


原Sybase SP代码:
SELECT * FROM hospital WHERE code = ''; -- 注意,这里是空字符串('')

以下是采用工具自动转换后的PG代码:
SELECT * FROM hospital WHERE code = ' ';  -- 转换后,变成一个空格(' ')的字符串

这说明自动转换工具知道Sybase的空字符串('')不空,而是一个空格,因此转换后的代码自动使用空格替代。

但实际上这样转换是问题的。我们先查看Sybase的hospital表,发现插入的code为空字符串时,select出来的code确实变为一个空格(' ')。我们再看PG的hospital表,因为自动迁移工具知道在空格(' ')在Sybase中是空字符的意思,因此数据迁移到PG后,PG的hospital表中的记录就是空字符串,而不是空格(' ')。然后这行代码就崩溃了:
SELECT * FROM hospital WHERE code = ' ';


因为PG里select出来的code是空字符串(''),永远不会与空格(' ')相等!

那如何修复?当然是将PG的代码使用空字符串('')替代空格(' ')。

案例三:传入参数为空字符串引发的问题


再看一个SP的入参问题。遇到过一个类似下面的Sybase的SP:
CREATE PROCEDURE prc 

@search_string VARCHAR(10)

AS SELECT * FROM hospital WHERE code LIKE '%' + @search_string + '%'

转换为PG的SP后,代码为:
CREATE OR REPLACE FUNCTION prc ( search_string VARCHAR(10) )

RETURNS SETOF hospital

AS $$ 

BEGIN

    RETURN query SELECT * FROM hospital WHERE code LIKE '%'||  search_string || '%';

END;

$$

LANGUAGE PLPGSQL;

转换后的代码看起来没有错,执行也没报错。但其实转换后的SP行为已经与Sybase的不一样了!让我们使用空字符串作为参数分别调用一下这两个sp,就会发现Sybase的没有任何数据返回,而PG的则返回了所有记录。为何?因为在Sybase中,输入空字符串后,Sybase会解释为空格,因此SQL变为:
SELECT * FROM hospital WHERE code LIKE  '% %'; -- 查带有空格的code,肯定查不到数据。


而PG呢,输入空字符串后,SQL就是
SELECT * FROM hospital WHERE code LIKE  '%%';  -- 就是查询所有数据。

为了保证移植后的SP行为一致性,需要在PG的SP代码中将空字符串''转换成空格' '。

以上便是我们项目组看到过的由Sybase空字符串引发的问题汇总,现存档供大家分析参考。