char、varchar、text和nchar、nvar

本文详细解释了数据库设计中常用的数据类型CHAR、VARCHAR、TEXT与NCHAR、NVARCHAR、NTEXT的区别与应用场景,包括定长与变长数据存储、Unicode字符集处理等要点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
转发
char、varchar、text和nchar、nvarchar、ntext的区别
    很多开发者进行数据库设计的时候往往并没有太多的考虑char, varchar类型,有的是根本就没注意,因为存储价格变得越来越便宜了,忘记了最开始的一些基本设计理论和原则,这点让我想到了现在的年轻人,大手一挥一把人民币就从他手里溜走了,其实我想不管是做人也好,做开发也好,细节的把握直接决定很多东西。当然还有一部分人是根本就没弄清楚他们的区别,也就随便选一个。在这里我想对他们做个简单的分析,当然如果有不对的地方希望大家指教。
1、CHAR。CHAR存储定长数据很方便,CHAR字段上的索引效率级高,比如定义char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间,不足的自动用空格填充,所以在读取的时候可能要多次用到trim()。

2、VARCHAR。存储变长数据,但存储效率没有CHAR高。如果一个字段可能的值是不固定长度的,我们只知道它不可能超过10个字符,把它定义为 VARCHAR(10)是最合算的。VARCHAR类型的实际长度是它的值的实际长度+1。为什么“+1”呢?这一个字节用于保存实际使用了多大的长度。从空间上考虑,用varchar合适;从效率上考虑,用char合适,关键是根据实际情况找到权衡点。

3、TEXT。text存储可变长度的非Unicode数据,最大长度为2^31-1(2,147,483,647)个字符。

4、NCHAR、NVARCHAR、NTEXT。这三种从名字上看比前面三种多了个“N”。它表示存储的是Unicode数据类型的字符。我们知道字符中,英文字符只需要一个字节存储就足够了,但汉字众多,需要两个字节存储,英文与汉字同时存在时容易造成混乱,Unicode字符集就是为了解决字符集这种不兼容的问题而产生的,它所有的字符都用两个字节表示,即英文字符也是用两个字节表示。nchar、nvarchar的长度是在1到4000之间。和char、varchar比较起来,nchar、nvarchar则最多存储4000个字符,不论是英文还是汉字;而char、varchar最多能存储8000个英文,4000个汉字。可以看出使用nchar、nvarchar数据类型时不用担心输入的字符是英文还是汉字,较为方便,但在存储英文时数量上有些损失。

所以一般来说,如果含有中文字符,用nchar/nvarchar,如果纯英文和数字,用char/varchar

我把他们的区别概括成:
CHAR,NCHAR 定长,速度快,占空间大,需处理
VARCHAR,NVARCHAR,TEXT 不定长,空间小,速度慢,无需处理
NCHAR、NVARCHAR、NTEXT处理Unicode码

DECLARE v_max_add_time TIMESTAMP; v_check_time TIMESTAMP := SYSTIMESTAMP; -- 统一检测时间 v_sql VARCHAR2(4000); v_actual_value NVARCHAR2(4000); v_null_token CONSTANT NVARCHAR2(20) := '<<<NULL>>>'; -- 特殊标记用于空值比较 TYPE result_rec IS RECORD ( order_no NVARCHAR2(64), tb_name NVARCHAR2(128), col_name NVARCHAR2(128), cf_value NVARCHAR2(512), add_time TIMESTAMP, scene_code NVARCHAR2(32), scene_name NVARCHAR2(128), strategy_id NUMBER(19), strategy_name NVARCHAR2(128), rule_id NUMBER(19), rule_name NVARCHAR2(128) ); TYPE result_tab IS TABLE OF result_rec; v_results result_tab; -- 检查表名是否存在的函数 FUNCTION table_exists(p_table_name VARCHAR2) RETURN BOOLEAN IS v_count NUMBER; BEGIN SELECT COUNT(*) INTO v_count FROM all_tables WHERE table_name = UPPER(p_table_name) AND owner = 'GHANA_RISK'; -- 替换为实际schema名 RETURN (v_count > 0); EXCEPTION WHEN OTHERS THEN RETURN FALSE; END; -- 检查列名是否存在的函数 FUNCTION column_exists(p_table_name VARCHAR2, p_column_name VARCHAR2) RETURN BOOLEAN IS v_count NUMBER; BEGIN SELECT COUNT(*) INTO v_count FROM all_tab_columns WHERE table_name = UPPER(p_table_name) AND column_name = UPPER(p_column_name) AND owner = 'GHANA_RISK'; -- 替换为实际schema名 RETURN (v_count > 0); EXCEPTION WHEN OTHERS THEN RETURN FALSE; END; -- 获取列数据类型函数 FUNCTION get_column_data_type( p_table_name VARCHAR2, p_column_name VARCHAR2 ) RETURN VARCHAR2 IS v_data_type VARCHAR2(128); BEGIN SELECT data_type INTO v_data_type FROM all_tab_columns WHERE owner = 'GHANA_RISK' AND table_name = UPPER(p_table_name) AND column_name = UPPER(p_column_name); RETURN v_data_type; EXCEPTION WHEN OTHERS THEN RETURN 'UNKNOWN'; END; BEGIN -- 1. 获取最新检测时间范围 SELECT MAX(ADD_TIME) INTO v_max_add_time FROM GHANA_RISK.TQ_RULE_ITEM_RESULT_7; -- 2. 获取半小时内需检测的数据 SELECT TQ_ORDER_NO, TB_NAME, COL_NAME, CF_VALUE, ADD_TIME, SCENE_CODE, SCENE_NAME, STRATEGY_ID, STRATEGY_NAME, RULE_ID, RULE_NAME BULK COLLECT INTO v_results FROM GHANA_RISK.TQ_RULE_ITEM_RESULT_7 WHERE ADD_TIME BETWEEN v_max_add_time - INTERVAL '10' MINUTE AND v_max_add_time; -- 3. 遍历每条记录并动态检测 FOR i IN 1..v_results.COUNT LOOP DECLARE v_data_type VARCHAR2(128); v_conversion_expr VARCHAR2(100); BEGIN -- 验证表名列名是否存在 IF NOT table_exists(v_results(i).tb_name) THEN RAISE_APPLICATION_ERROR(-20001, 'Table not found: ' || v_results(i).tb_name); END IF; IF NOT column_exists(v_results(i).tb_name, v_results(i).col_name) THEN RAISE_APPLICATION_ERROR(-20002, 'Column not found: ' || v_results(i).col_name); END IF; -- 获取列数据类型并构建转换表达式 v_data_type := get_column_data_type(v_results(i).tb_name, v_results(i).col_name); CASE v_data_type WHEN 'NUMBER' THEN -- 数字类型:使用TO_CHAR保留格式(包括小于1的前导0) v_conversion_expr := 'TO_CHAR(' || v_results(i).col_name || ', ''FM9999999999999999999990D99999999999999999999'')'; WHEN 'VARCHAR2', 'CHAR', 'NVARCHAR2', 'NCHAR' THEN -- 字符类型:直接使用原列 v_conversion_expr := v_results(i).col_name; ELSE -- 其他类型:默认转换为字符串 v_conversion_expr := 'TO_CHAR(' || v_results(i).col_name || ')'; END CASE; -- 构建动态SQL(转换为NVARCHAR2以匹配cf_value类型) v_sql := 'SELECT TO_NCHAR(' || v_conversion_expr || ') ' || 'FROM GHANA_RISK.' || v_results(i).tb_name || ' ' || 'WHERE TQ_ORDER_NO = :1'; -- 执行动态查询 EXECUTE IMMEDIATE v_sql INTO v_actual_value USING v_results(i).order_no; -- 4. 比较值并插入差异 IF NVL(v_actual_value, v_null_token) <> NVL(v_results(i).cf_value, v_null_token) THEN INSERT INTO FK_ADS.ADS_MONITORING_RESULT_DI ( TQ_ORDER_NO, TB_NAME, COL_NAME, CF_VALUE, ACTUAL_VALUE, CHECK_TIME, ADD_TIME, SCENE_CODE, SCENE_NAME, STRATEGY_ID, STRATEGY_NAME, RULE_ID, RULE_NAME ) VALUES ( v_results(i).order_no, v_results(i).tb_name, v_results(i).col_name, v_results(i).cf_value, v_actual_value, v_check_time, v_results(i).add_time, v_results(i).scene_code, v_results(i).scene_name, v_results(i).strategy_id, v_results(i).strategy_name, v_results(i).rule_id, v_results(i).rule_name ); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN INSERT INTO FK_ADS.ADS_MONITORING_RESULT_DI ( TQ_ORDER_NO, TB_NAME, COL_NAME, CF_VALUE, ACTUAL_VALUE, CHECK_TIME, ADD_TIME, SCENE_CODE, SCENE_NAME, STRATEGY_ID, STRATEGY_NAME, RULE_ID, RULE_NAME ) VALUES ( v_results(i).order_no, v_results(i).tb_name, v_results(i).col_name, v_results(i).cf_value, 'RECORD_NOT_FOUND', v_check_time, v_results(i).add_time, v_results(i).scene_code, v_results(i).scene_name, v_results(i).strategy_id, v_results(i).strategy_name, v_results(i).rule_id, v_results(i).rule_name ); COMMIT; END; /报错[65000][6550] ORA-06550: 第 116 行, 第 24 列: PLS-00103: 出现符号 ","在需要下列之一时: * & = - + < / > at in is mod remainder not rem then <an exponent (* ...
最新发布
07-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值