Oracle中CHAR (n)和VARCHAR2(n)的区别

本文探讨了在使用Char类型作为ID字段时遇到的查询问题,特别是在Hibernate框架下出现ObjectNotFoundException的原因及解决方法。通过调整SQL查询语句,成功解决了由于固定长度字符导致的数据查询错误。
今天在做删除歌曲的时候,出现org.hibernate.ObjectNotFoundException: No row with the given identifier exists:[cn..entity.Song#1]的问题,不过查看表后,id的值是存在的。

经过一番查找,发现表中id字段的类型是CHAR(22),以前记得跟VARCHAR2有所区别,即CHAR 的长度是固定的,而VARCHAR2的长度是可以变化的, 比如,存储字符串“abc",对于CHAR (20),表示你存储的字符将占20个字节(包括17个空字符),而同样的VARCHAR2 (20)则只占用3个字节的长度,20只是最大值,当你存储的字符小于20时,按实际长度存储。

其中后台产生的SQL如下:
select song0_.ID as ID7_0_,song0_.TITLE as TITLE7_0_ from SONG song0_ where song0_.ID='1';
由上面可知,虽然你看到表中有id值为1,实际上它的值因为1+19个空格的长度,所以查找不到,如:

[url=http://hongfanshu.blogbus.com][img]http://dl.iteye.com/upload/attachment/349308/c765dc20-1a62-3080-9dcf-895f6a31381e.jpg[/img][/url]

[url=http://hongfanshu.blogbus.com][img]http://dl.iteye.com/upload/attachment/349316/73f1ff6b-140f-35c3-96ca-b1d67d201de2.jpg[/img][/url]

[url=http://hongfanshu.blogbus.com][img]http://dl.iteye.com/upload/attachment/349310/8d12a344-d49b-3e54-b0e8-13a64e4c9883.jpg[/img][/url]

[url=http://hongfanshu.blogbus.com][img]http://dl.iteye.com/upload/attachment/349306/f0229f4d-5c76-32c1-ab6f-2c3c7636c0be.jpg[/img][/url]

[url=http://hongfanshu.blogbus.com][img]http://dl.iteye.com/upload/attachment/349313/87f26ea2-75c1-3dbf-89ff-706534b9cd79.jpg[/img][/url]

select * from song t where t.id = '1' 可以查到数据
select * from song t where t.id = '1 '可以查到数据

select * from song t where t.title = 'lady gaga'可以查到数据
select * from song t where t.title = 'lady gaga '不可以查到数据

有空,欢迎到[url=http://hongfanshu.blogbus.com/]红番薯[/url]逛逛
<think> 我们参考了用户提供的引用内容,特别是引用[1]中提到了to_char函数用于将数字转换为字符串,但用户的问题是关于CHAR类型转换为VARCHAR2类型。 另外,引用[3]中提到了CHARVARCHAR2在定义上的区别,但没有直接给出转换方法。 实际上,在Oracle中,CHARVARCHAR2都是字符类型,它们之间可以直接转换,但需要注意CHAR类型是固定长度,而VARCHAR2是可变长度。 转换方法: 1. 在查询中直接使用表达式转换:使用CAST函数或直接赋值(因为Oracle会自动进行字符类型转换,但需要注意空格填充问题)。 2. 使用TO_CHAR函数(虽然主要用于数字或日期转字符串,但也可以用于字符类型之间的转换,不过这里不是必须的)。 3. 注意:CHAR类型在存储时如果长度不足,会用空格填充到指定长度,而VARCHAR2不会。因此,在转换时可能需要去掉多余的空格。 具体方法: 方法一:使用CAST函数 SELECT CAST(char_column AS VARCHAR2(10)) FROM table; 方法二:直接赋值(在PL/SQL或查询中) SELECT TRIM(char_column) FROM table; -- 先去掉空格,然后自动转为VARCHAR2 或者直接:SELECT char_column FROM table; -- 在需要VARCHAR2的地方,Oracle会自动将CHAR转换为VARCHAR2(但会保留空格,所以通常需要TRIM) 但是,如果直接转换,CHAR类型的值后面可能会有空格,所以为了得到VARCHAR2完全一致的值(即去掉尾部空格),我们通常使用RTRIM或TRIM。 另外,在表结构设计时,如果要将CHAR类型的列改为VARCHAR2类型,可以使用ALTER TABLE语句修改列的数据类型(但要注意数据长度是否允许为空等约束)。 修改表结构的SQL: ALTER TABLE table_name MODIFY (column_name VARCHAR2(new_size)); 注意:在修改列类型时,如果表中已有数据,需要确保转换是可行的(通常CHARVARCHAR2没有问题,因为VARCHAR2的存储方式更节省空间,但要注意长度,VARCHAR2的最大长度要设置得足够大,至少不能小于原CHAR列中实际数据的最大长度)。 所以,根据用户需求,总结如下: 1. 在查询中转换,可以使用CAST或者TRIM函数。 2. 修改表结构,使用ALTER TABLE语句。 下面我们给出具体示例。 示例1:在查询中转换 假设有一张表t1,其中有一列c1是CHAR(10)类型,我们想查询为VARCHAR2类型(去掉尾部空格): SELECT RTRIM(c1) AS c1_varchar2 FROM t1; 或者 SELECT CAST(c1 AS VARCHAR2(10)) AS c1_varchar2 FROM t1; -- 注意:这样转换后,尾部空格依然存在,所以通常结合TRIM使用 实际上,CAST(c1 AS VARCHAR2(10)) 会保留尾部空格,所以如果要去掉空格,可以: SELECT TRIM(CAST(c1 AS VARCHAR2(10))) FROM t1; 但更简单的是:SELECT TRIM(c1) FROM t1; -- 这样得到的就是VARCHAR2类型(去掉尾部空格) 示例2:修改表结构 将表t1的列c1从CHAR(10)修改为VARCHAR2(10): ALTER TABLE t1 MODIFY (c1 VARCHAR2(10)); 注意:修改表结构时,如果该列有数据,并且数据长度超过VARCHAR2定义的长度,会报错。所以确保VARCHAR2的长度足够。 另外,在Oracle中,VARCHAR2的最大长度是4000字节(在12c以上版本,如果开启扩展数据类型,可以更大)。 因此,针对用户的问题,我们给出两种场景的解决方案:查询时转换修改表结构。 由于用户没有明确是哪种场景,我们两种都提供。 最后,根据引用[2]中提到的,VARCHAR2是可变长度,不会用空格填充,所以在转换后可以节省空间,并且避免空格带来的比较问题。 </think>在Oracle中将CHAR类型转换为VARCHAR2类型有两种主要方法,具体选择取决于实际需求: ### 方法1:使用CAST函数(推荐) ```sql SELECT CAST(char_column AS VARCHAR2(50)) AS varchar_column FROM your_table; ``` **说明**: - 直接使用CAST函数显式转换数据类型 - `VARCHAR2(50)`中的长度需根据实际数据设定(参考原CHAR列定义) - 转换后会**保留尾部空格**,因为CHAR类型是固定长度(例如`'ABC '`会保持原样)[^2][^3] ### 方法2:使用RTRIM函数(去除尾部空格) ```sql SELECT RTRIM(char_column) AS varchar_column FROM your_table; ``` **说明**: - `RTRIM()`会移除CHAR类型自动填充的尾部空格 - 转换结果为`VARCHAR2`类型(长度自适应) - 适用于需要精确字符串比较的场景(例如`'ABC'``'ABC '`将被视为相同)[^1][^2] --- ### 重要注意事项 1. **数据截断风险** 若原CHAR列包含超过目标VARCHAR2长度的数据,转换会导致截断: ```sql -- 安全做法:先验证最大长度 SELECT MAX(LENGTH(char_column)) FROM your_table; ``` 2. **表结构修改(DDL)** 永久性转换列类型需使用`ALTER TABLE`: ```sql ALTER TABLE your_table MODIFY (char_column VARCHAR2(50)); ``` **注意**: - 需要表级锁,建议在低峰期操作 - 必须确保新长度≥现有数据最大长度[^1][^3] 3. **隐式转换规则** 在SQL语句中混合使用CHARVARCHAR2时: ```sql SELECT * FROM table1 t1 JOIN table2 t2 ON t1.char_col = t2.varchar2_col; -- Oracle自动转换CHARVARCHAR2 ``` 但显式转换可避免隐式转换的性能开销[^2][^4] 4. **存储差异** | 类型 | 存储方式 | 示例数据 `'ABC'` | |------------|-------------------|------------------| | **CHAR(10)** | 固定长度+空格填充 | `'ABC '` | | **VARCHAR2** | 变长(无填充) | `'ABC'` | 转换后存储空间通常减少30%-50%[^2] --- ### 应用场景建议 - 需要**精确字符串匹配** → 用`RTRIM()`去除空格 - 需**保留原始格式** → 用`CAST()` - **高频查询字段** → 优先转VARCHAR2提升性能 - **数据迁移/集成** → 统一为VARCHAR2减少类型冲突[^3][^5]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值