Oracle 中文记录 及 乱码 判断 说明


 

一. 判断记录中是否有中文

这个可以使用Length 和 Lengthb 函数。 这个在blog里有说明:

Oracle Length 和 Lengthb 函数说明

http://www.cndba.cn/Dave/article/1126

 

            这种方法使用与字符集为GBK的情况,其他情况不使用。在GBK 字符集下,中文的length 是1个字符,而lengthb 是2个byte。

 

示例:

select * from t where length(c1) != lengthb(c1);

 

 

二. 判断记录中是否有乱码

这里需要使用asciistr 函数,ASCIISTR 的官网说明:

 

http://download.oracle.com/docs/cd/E11882_01/server.112/e26088/functions014.htm#i1000190

 

ASCIISTR takesas its argument a string, or an expression that resolves to a string, in anycharacter set and returns an ASCII version of the string in the databasecharacter set. Non-ASCII characters are converted to the form \xxxx, where xxxx representsa UTF-16 code unit.

            ASCIISTR函数会把非ASCII 的字符转换成\xxxx 的格式,xxxx 是UTF-16的code unit。

           

示例:

SQL> select asciistr('/\/Davecome from 安?庆?')  as str from dual;

/\005C/Dave come from \5B89?\5E86?

 

这里的’安’ 被转换成了\5B89,‘庆’被转换成了\5E86。

 

这里要注意一个特殊字符“\”,当它出现的时候转换后的码为“\005C”。

 

 

当然,我们也可以使用UNISTR函数,把asciistr 的结果反转回来,如:

SQL>select UNISTR('\5E86') from dual;

 

那么当我们的记录中存在中文乱码时,

select UNISTR('\FFFD') from dual;

 

注意一点:

执行以上SQL 不要在sqlplus 里执行,sqlplus 受本地环境影响。到第三方的工具(PL/SQL DEV 或者Toad)里测试。

 

那么当我们的中文记录变成乱码后,那么转成asciistr的值就会包含2种特殊符号: ? 和 \FFFD 对应的问号。我们只需要匹配这2种符号,就可以判断记录里是否有乱码了。

 

 

HelloJin 同学提供的判断乱码的存储过程:

 

/* Formatted on 2011/10/11 16:03:44 (QP5 v5.163.1008.3004) */
CREATE OR REPLACE PROCEDURE MessyCode (pv_tbname   IN VARCHAR2,
                                       pv_owner    IN VARCHAR2)
IS
   num        NUMBER;
   p_flag     NUMBER;
   p_flag2    NUMBER;
   p_flagw1   NUMBER;
   p_flagw2   NUMBER;
   p_col      VARCHAR2 (30);
   p_str      VARCHAR2 (1000);
   p_tbname   VARCHAR2 (30);
   p_owner    VARCHAR2 (30);
BEGIN
   p_tbname := pv_tbname;
   p_owner := pv_owner;
   p_flagw1 := 0;
   p_flagw2 := 0;

   --  p_tbname:='LANK_F1';
   --  p_owner:='SYS';
   --dbms_output.put_line(p_tbname||p_owner);
   SELECT MAX (COLUMN_ID)
     INTO p_flag
     FROM dba_tab_columns
    WHERE table_name = p_tbname AND owner = p_owner;

   -- dbms_output.put_line('Total:'||p_flag);
   EXECUTE IMMEDIATE 'alter session enable parallel dml';

   WHILE p_flag > 0
   LOOP
      SELECT column_name
        INTO p_col
        FROM dba_tab_columns
       WHERE table_name = p_tbname AND COLUMN_ID = p_flag AND owner = p_owner;

--用来判断是否有中文,并且表的记录数小于<10000
      EXECUTE IMMEDIATE
            'select sum(length('
         || p_col
         || ')) , sum(lengthb('
         || p_col
         || ')) from '
         || p_owner
         || '.'
         || p_tbname
         || '  where rownum<10000'
         INTO p_flagw1, p_flagw2;

--如果有中文,在判断乱码,这里启用了8个并行查询
      -- dbms_output.put_line(p_str);
      IF p_flagw1 <> p_flagw2
      THEN
         EXECUTE IMMEDIATE
               'select  /*+ parallel('
            || p_tbname
            || ',8) */  count('
            || p_col
            || ') from  '
            || p_owner
            || '.'
            || p_tbname
            || ' where asciistr('
            || p_col
            || ') like ''%??%'' or asciistr('
            || p_col
            || ') like ''%\FFFD%''  '
            INTO p_flag2;

         IF p_flag2 > 0
         THEN
            DBMS_OUTPUT.put_line (
                  p_owner
               || '.'
               || p_tbname
               || ' Have:'
               || p_flag
               || ','
               || p_col);

            INSERT INTO resout
                 VALUES (p_owner,
                         p_tbname,
                         p_flag,
                         p_col);

            COMMIT;
         END IF;
      END IF;

      --init
      p_flag2 := 0;
      p_flag := p_flag - 1;
   END LOOP;

   INSERT INTO table_temp
        VALUES (p_owner, p_tbname);

   COMMIT;
END MessyCode;


该存储过程由HelloJin同学编写,感谢他的辛勤劳动。

 

这里的核心语句是:

            || ' where asciistr('
            || p_col
            || ') like ''%??%'' or asciistr('
            || p_col
            || ') like ''%\FFFD%''  '
            INTO p_flag2;


Oracle Parallel Execution(并行执行)

http://www.cndba.cn/Dave/article/1573

 

如果是因为复制导致的乱码,可以和源库进行匹配,然后使用replace函数替换掉乱码。

 

下面的SQL 可以处理库之间复制导致的乱码:

 

/* Formatted on 2011/10/11 20:29:00 (QP5 v5.163.1008.3004) */
DECLARE
   exec_block     VARCHAR2 (32767);
   module_block   VARCHAR2 (32767)
      := '
  declare      cur_ucs sys_refcursor;
  type type_tab_ucs1 is table of $$1.$$2.$$3%type;
  v_tab_ucs1 type_tab_ucs1;
  type type_tab_pk1 is table of $$1.$$2.$$4%type;
  v_tab_pk1 type_tab_pk1;

begin
  open cur_ucs for select $$3,$$4 from $$1.$$2@$$remotelink;
  loop
   fetch cur_ucs bulk collect into v_tab_ucs1,  v_tab_pk1 limit 10000;
  exit when v_tab_ucs1.count = 0;

  forall i in 1 .. v_tab_ucs1.count
    update  $$1.$$2
       set  $$1.$$2.$$3  = v_tab_ucs1(i)
     where $$1.$$2.$$4 = v_tab_pk1(i);
  commit;
end loop;
close cur_ucs;
end; ';
BEGIN
   FOR i IN (SELECT table_name, col_name FROM tab_col)
   LOOP
      exec_block := REPLACE (module_block, '$$1', i.owner);
      exec_block := REPLACE (module_block, '$$2', i.table_name);

      EXECUTE IMMEDIATE exec_block;

      INSERT INTO recordupdate
           VALUES (i.owner, i.table_name, i.col_name);
   END LOOP;
END;


该代码同样由HelloJin 同学提供。

 


 

其他文档:

Common Character Sets Found in InternetEmails [ID 477967.1]

 

 

 

 

-------------------------------------------------------------------------------------------------------

QQ:492913789

Email:ahdba@qq.com

Blog:  http://www.cndba.cn/dave

Weibo:    http://weibo.com/tianlesoftware

Twitter:  http://twitter.com/tianlesoftware

Facebook: http://www.facebook.com/tianlesoftware

Linkedin: http://cn.linkedin.com/in/tianlesoftware


DBA1 群:62697716(满);   DBA2 群:62697977(满)  DBA3 群:62697850(满)  

DBA 超级群:63306533(满);  DBA4 群:83829929(满) DBA5群: 142216823(满) 

DBA6 群:158654907(满)   DBA7 群:69087192(满)  DBA8 群:172855474

DBA 超级群2:151508914  DBA9群:102954821     聊天 群:40132017(满)

--加群需要在备注说明Oracle表空间和数据文件的关系,否则拒绝申请


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值