• [迁移系列] GaussDB和Oracle DDL的差异和替换示例
    DDL语法OracleGaussDB A处理方案示例create table支持pctfree、initrans、maxtrans、storage属性。不支持。删除。影响:不具备类似Oracle中pctfree、initrans、maxtrans控制存储方式的功能。-支持字段和表名被双引号包含,Oracle默认全部转换为大写支持字段和表名被双引号包含,但是GaussDB A默认全部转换为小写,且加引号导致大小写敏感去掉引号。Oracle:CREATE TABLE "CTRM2"."TEMP_LGH"( "IMEI" VARCHAR2(20 BYTE) NOT NULL ENABLE,"MOBILE_NO" VARCHAR2(20 BYTE),"SALESTIME" DATE,);GaussDB A:CREATE TABLE CTRM2.TEMP_LGH( IMEI VARCHAR2(20 ) NOT NULL ENABLE,MOBILE_NO VARCHAR2(20 ),SALESTIME DATE,);[l(1]List分区表list类型分区表。不支持。改为范围分区。Oracle:CREATE TABLE rp_t_trans(  POLICYNO       VARCHAR2(22) not null,  COMID          VARCHAR2(8),)PARTITION BY LIST (COMID)(  PARTITION par_1100 VALUES ('11000000')    TABLESPACE report,  PARTITION par_1200 VALUES ('12000000')    TABLESPACE report,  PARTITION par_1300 VALUES ('13000000')    TABLESPACE report,  PARTITION p_other VALUES (DEFAULT)    TABLESPACE report);GaussDB A:CREATE TABLE rp_t_trans(  POLICYNO       VARCHAR2(22) NOT NULL,  COMID          VARCHAR2(8),)PARTITION BY RANGE(COMID)(  PARTITION par_1100VALUES LESS THAN ('11000000'),  PARTITION par_1200VALUES LESS THAN ('12000000'),  PARTITION par_1300VALUES LESS THAN ('13000000'),  PARTITION p_otherVALUES LESS THAN (MAXVALUE));create indextype创建索引类型。不支持。删除。影响:无索引类型对象。-create indexl  分区表支持全局索引。l  索引可以加模式名指定索引所属的模式。l  不支持全局索引。l  不支持组织索引。l  索引名前不能带schema名称。l  HASH表中唯一索引必须包含分布列,分区表唯一索引必须包含分布列和分区键。复制表无此限制。l  替换全局索引为分区索引。l  删除索引前的模式名。l  根据约束整改。-constraint无要求。l  不支持外键约束。l  unique约束必须包含分布列。l  列存不支持UNIQUE/PRIMARY KEY。l  不支持pctfree。l  add check 后面不能添加enable。l  约束名不能和表名和索引同名。根据约束要求整改。-sequence序列。Oracle访问方式如下:sequence_name.NEXTVALGaussDB A访问方式如下:NEXTVAL(‘sequence_name’)替换访问方式。-view视图。l  不支持force view。l  不支持视图名带有字符@。l  不支持sys_guid()。l  不支持给视图中插入数据。l  物化视图。l  删除。l  修改视图名称不包含@。l  修改为sequence,为每个视图创建Sequence,采用seq+视图名的方式命名。l  直接将数据插入至视图底层的表中。l  替换为普通视图。-自定义类型支持。不支持。将自定义类型替换为自定义函数。GaussDB A:DROP FUNCTION test_func(str text, delimter text) ; CREATE FUNCTION test_func(str text, delimter text)RETURNS setof intAS $$declare    arr text array;    result text;    item text;    sumnum int :=0;begin    select regexp_split_to_array(str,delimter) into arr from dual;    foreach item in array arr    loop        sumnum := sumnum+to_number(item);                if result is null then                        result := to_char(sumnum);                else                        result := result || delimter || to_char(sumnum);                end if;    --    raise info 'item: %  result %', item, sumnum;    end loop;         return query with t(a) as (select regexp_split_to_table(result, delimter)) select a::int from t;end;$$language plpgsql; select test_func('11#11#11','#');select string_agg(a, ',') from  (values ('aa'), ('bb'), ('cc')) as a(a) ;================================================= CREATE OR REPLACE FUNCTION public.test_func(str text, delimter text) RETURNS SETOF integer LANGUAGE plpgsql FENCEDAS $function$declare    arr text array;    result text;    item text;    sumnum int :=0;begin    select regexp_split_to_array(str,delimter) into arr from dual;    foreach item in array arr    loop        sumnum := sumnum+to_number(item);                if result is null then                        result := to_char(sumnum);                else                        result := result || delimter || to_char(sumnum);                end if;    --    raise info 'item: %  result %', item, sumnum;    end loop;         return query with t(a) as (select regexp_split_to_table(result, delimter)) select a::int from t;end;$function$package支持。不支持。替换为schema。变量放在函数里。Oracle:--存储过程和函数。function/procedure secu_f_global_setparm()--自定义游标。CREATE OR REPLACE PROCEDURE pkg_fasp_gl. checkdatas(                                    ....                      o_result_cur    out refcursor);--存储过程中引用包的全局变量。FUNCTION secu_f_global_setparm(v_userida varchar2,v_pmdivida varchar2,v_pmyeara varchar2,v_pmyaa varchar2 default null)  return varchar2 is      begin                v_userid:=v_userida;--调用存储过程。deleteglvouchersnolock;GaussDB A:--在数据库中创建和包名同名的schema。CREATE SCHEMA global_multyear_cz;--创建存储过程和函数。CREATE OR REPLACE FUNCTION/PROCEDURE global_multyear_cz.secu_f_global_setparm();--自定义游标。CREATE OR REPLACE PROCEDURE pkg_fasp_gl. checkdatas(                                    ....                      o_result_cur    out refcursor);--存储过程中引用包的全局变量。CREATE OR REPLACE FUNCTION global_multyear_cz.secu_f_global_setparm(v_userida varchar2,v_pmdividavarchar2,v_pmyeara varchar2,v_pmyaa varchar2 default null)  return varchar2 is      v_userid  varchar2(32);      begin--调用存储过程。deleteglvouchersnolock();procedure支持变量名和表定义字段名相同不支持变量名和表定义字段名相同GaussDB不允许存储过程中出现表字段名和变量名相同的情况,建议变量使用不同于字段的名称。Oracle:CREATE OR replace PROCEDURE ctrm3.s_insert_s_t_status_2015 ( v_settlemon IN varchar2 ) IS sql_query varchar2 ( 500 ) ;isdelete NUMBER ;BEGINsql_query := ' select sc.settleobjorg_id,sc.isdelete from settleobj_confirm sc ' ;OPEN cur_rs1 FOR sql_query ;LOOP…FETCH cur_rs1 INTO orgid,isdelete;EXIT…END ;/GaussDB A:CREATE OR replace PROCEDURE ctrm3.s_insert_s_t_status_2015 ( v_settlemon IN varchar2 ) IS sql_query varchar2 ( 500 ) ;isdelete1 NUMBER ;BEGINsql_query := ' select sc.settleobjorg_id,sc.isdelete from settleobj_confirm sc ' ;OPEN cur_rs1 FOR sql_query ;LOOP…FETCH cur_rs1 INTO orgid,isdelete1;EXIT…END ;/[l(1]视图user_view_tab_columns支持。不支持。使用如下自定义视图代替。CREATE VIEW  user_view_tab_columns asSELECT dba_view_columns.owner, dba_view_columns.table_name,dba_view_columns.column_name,dba_view_columns.data_type,dba_view_columns.column_id, dba_view_columns.data_length,dba_view_columns.comments  FROM dba_view_columnsUNION SELECT dba_tab_columns.owner, dba_tab_columns.table_name,dba_tab_columns.column_name, dba_tab_columns.data_type,dba_tab_columns.column_id, dba_tab_columns.data_length,dba_tab_columns.comments FROM dba_tab_columns;-
  • [迁移系列] Gauss 和 Oracle DML的差异和规避方案
    DML语法OracleGaussDB A处理方案示例ROWNUM表示行号。不支持。使用limit分页。Oracle:SELECT * FROM tbl WHERE rownum = 1;GaussDB A:SELECT * FROM tbl limit 1;limit限制返回行数。Oracle语法如下:LIMIT num1,num2支持,语法有差异。GaussDB A使用语法如下:OFFSET num1 LIMIT num2替换为GaussDB A的语法。Oracle:SELECT * FROM table limit 1, 10;GaussDB A:SELECT * FROM table offset 1 limit 10;ROWIDrowid保证记录的唯一性。采用xc_node_id||tabeoid||ctid以保证全局的唯一性。xc_node_id是指DN编号,tableoid是指表的oid(同表不同分区也不一样),ctid是指行的oid,该值非自增。xc_node_id||tabeoid||ctid标识是全局唯一,生成后值不变,对用户只读,不可更改。替换为GaussDB A的语法。-autocommit自动提交。不支持。如果有需要,可以显式的通过如下语法实现:start transaction;…commit;-trigger触发器。不支持。删除-merge into合入。MERGE INTO products pUSING newproducts npON (p.product_id = np.product_id)WHEN MATCHED THENUPDATESET p.product_name = np.product_name;不支持。使用UPDATE和INSERT语句替换。START TRANSACTION;CREATE UNLOGGED TABLE tmp_products AS TABLE products WITH NO DATA ;INSERT INTO tmp_products SELECT p.product_id, np.product_name FROM products p INNER JOIN newproducts np ON p.product_id = np.product_id;DELETE FROM products WHERE product_id IN (select product_id from newproducts);INSERT INTO products SELECT * FROM tmp_products;DROP TABLE tmp_products;COMMIT;-+支持Merge into中目标表为子查询不支持GaussDB A不支持merge into中目标表为子查询。Oracle:merge into (select *from terminalattributedetail ta1where ta1.columnname = 'supportimeitype') tausing (select t.id, decode(tt.test7, '单IMEI', '1', '双IMEI', '2') imeitypefrom terminal t, temp_test_20140702_a ttwhere t.name = tt.test1) rson (rs.id = ta.TERMINALID)when matched thenupdate set ta.value = rs.imeitypewhen not matched theninsertvalues(seq_terminalattdetail_id.nextval,rs.id,'supportimeitype',rs.imeitype);commit;GaussDB A:merge into terminalattributedetail taiusing (select t.id, decode(tt.test7, '单IMEI', '1', '双IMEI', '2') imeitypefrom terminal t, temp_test_20140702_a ttwhere t.name = tt.test1) rson (rs.id = ta.TERMINALID)when matched thenupdate set ta.value = rs.imeitype where ta1.columnname = 'supportimeitype'when not matched theninsertvalues(nextval('seq_terminalattdetail_id'),rs.id,'supportimeitype',rs.imeitype);[l(1]外连接。不支持。使用left outer join或right outer join替换。Oracle:SELECT ... FROM A,B WHERE A.ID=B.ID(+);GaussDB A:SELECT ... FROM A LEFT JOIN B ON A.ID=B.ID;grouping setsselect a, b, c, sum( d ) from t group by grouping sets ( a, b, c );不支持。select * from (select a, null, null, sum(d) from t group by aunion allselect null, b, null, sum(d) from t group by bunion allselect null, null, c, sum(d) from t group by c);-GroupingGrouping(c1)不支持。decode(c1,null,1,0)-select... from table(function(),’,’)支持。不支持。需要替换为SELECT REGEXP_SPLIT_TO_TABLE(ARRAY_RO_STRING(function(),’,’)’,’)’,’);Oracle:SELECT max(to_number(strvalue)) INTO v_levelcount         FROM table(fn_split(v_codemode, ‘-‘));GaussDB A:SELECT REGEXP_SPLIT_TO_TABLE(ARRAY_RO_STRING(fn_split(v_codemode, ‘-‘), ‘-‘), ‘-‘);connect by树形表数据子查询的连接语句。不支持。需要用with recursive用来替代Oracle语法中的connect by prior xxx start with yyy。Oracle:SELECT BA.ID FROM CTP_BRANCH BA WHERE BA.STATUS = ‘1’START WITH BA.ID = I_ORGBRANCHIDCONNECT BY PRIOR BA.ID = BA.PARENT_ID;GaussDB A:WITH RECURSIVE CTP_BRANCH01 AS(SELECT T.ID FROM SPMSADMIN.CTP_BRANCH T WHERE T.ID=I_ORGBRANCHIDUNION ALLSELECT T1.ID FROM SPMSADMIN.CTP_BRANCH T1 INNER JOIN CTP_BRANCH01 T2 ON T1.PARENT_ID=T2.ID)SELECT ID FROM CTP_BRANCH01 WHERE STATUS = ‘1’; union all支持前后的字段类型不一致是否可以自动转换不支持GaussDB A需要显示强转。Oracle:Select id, --number类型Name from test1Union allSelect null,Name from test2;GaussDB A:Select id, --number类型Name from test1Union allSelect null::number, --显示转换为number类型Name from test2;[l(1]DBMS_UTILITY.get_time支持获取时间差不支持使用extract(epoch from(current_timestamp)代替。Oracle:select DBMS_UTILITY.get_time from dual;GaussDB A:select round( floor(extract(epoch from(current_timestamp))),2 )[l(2]UPDATE更新表内容。l  不支持分布列更新。l  不支持查询子句中的limit。l  在遇到分布列必须更新时,需要将分布列换为业务中不会进行更新的列。l  在替换Oracle的update中的rownum时,需要借助with语句进行update。参见示例。Oracle:update TMP_COG_T_TEAMUSER_QRY a       SET (username,user_upperpath) = (SELECT        username,upperpath FROM rp_t_user b WHERE        a.comid = b.comid and a.usercode = b.usercode       and rownum <2 );GaussDB A:with tt AS (SELECT b.username,b.upperpath FROM rp_t_user b ,TMP_COG_T_TEAMUSER_QRY  a  WHERE a.comid = b.comid AND a.usercode = b.usercode limit 1)update TMP_COGT_TEAMUSER_QRY a SET (a.username,a.user_upperpath)=(b.username,b.upperpath)  FROM tt b;Order by无约束。l  不支持查询中使用了distinct或count等agg函数,但是order by后的列没有在select返回的目标列中。l  不支持某些排序参数。l  nls_sort参数小写不识别。l  不支持order by nlssort 函数后为asc nulls last。l  不支持nlssort函数在select列表中使用。l  删除order by后的列或者加一层查询,先order by再执行agg函数。l  选用其它的排序方式。l  nls_sort参数改为大写。l  order by nlssort 函数后为nulls last。l  使用其他SQL语句替代。l  Oracle:SELECT count(*) FROM spms_app_info ORDER BY app_id;GaussDB A:SELECT count(*) FROM spms_app_info;l  Oracle:SELECT b FROM test_a ORDER BY NLSSORT(b,'nls_sort=schinese_stroke_m');GaussDB A:SELECT b FROM test_a ORDER BY NLSSORT(b,'nls_sort=schinese_pinyin_m');l  Oracle:SELECT b FROM test_a ORDER BY NLSSORT(b,'nls_sort=schinese_stroke_m');GaussDB A:SELECT b FROM test_a ORDER BY NLSSORT(b,'nls_sort=SCHINESE_PINYIN_M') nulls last;l  Oracle:SELECT b FROM test_a ORDER BY NLSSORT(b,'nls_sort=SCHINESE_PINYIN_M') asc nulls last;GaussDB A:SELECT b FROM test_a ORDER BY NLSSORT(b,'nls_sort=SCHINESE_PINYIN_M') nulls last;l  Oracle:SELECT nlssort(b,'nls_sort=schinese_pinyin_m') FROM test_a;GaussDB A:SELECT b FROM test_a ORDER BY NLSSORT(b,'nls_sort=SCHINESE_PINYIN_M');全角字符支持全角字符直接在SQL中使用。不支持全角字符直接在SQL中使用。仅支持半角字符。全角字符替换为对应的半角字符。-Having子句选择符合条件的组。Oracle中语法如下:Having...Group by...支持Having子句,语法与Oracle不同。Group by...Having...调整位置。Oracle:SELECT t1.col1, t2.col2 , Aggregate_fn1, Aggregate_fn2 FROM tab1 t1, tab2 t2 WHERE … HAVING Aggregate_fnX <operator>  <value> GROUP BY t1.col1, t2.col2 ORDER BY … GaussDB A:SELECT t1.col1, t2.col2 , Aggregate_fn1, Aggregate_fn2 FROM tab1 t1, tab2 t2 WHERE … GROUP BY t1.col1, t2.col2 HAVING Aggregate_fnX <operator> <value>  ORDER BY … distinct支持在窗口函数中使用distinct。不支持。需要修改为先做group by求count(distinct),然后使用group by列做join列与原表join后获取到其他列的值。Oracle:SELECT distinct a, b, c, d, count(distinct a) over(partition by b) FROM test1;GaussDB A:SELECT a, A.b, A.c, A.d, B.count_distinct_a FROM (select distinct a,b,c,d from test1) AS A left join (select count(distinct a) count_distinct_a, b from test1 group by b) as B on   A.b=B.b or (A.b is NULL and B.b is NULL) order by b;列别名定义列别名时需要添加双引号。引用列别名时不需要加双引号。定义列别名时需要添加双引号,表明需要区分大小写。引用列别名时需要加双引号;引用时增加双引号。Oracle:select 企业客编CIS from (select cino as “企业客编CIS”from test );GaussDB A:select “企业客编CIS” from (select cino as “企业客编CIS”from test );ANY表达式Oracle表达式格式如下:any('number1','number2','number3'...)GaussDB A表达式格式如下:any(array('number1','number2','number3'...))将ANY表达式修改为GaussDB A的格式。Oracle:SELECT 1 FROM dual WHERE 11 = any(‘11’,’22’,’22’);GaussDB A:SELECT 1 FROM dual WHERE 11 = any(array(‘11’,’22’,’22’));或者SELECT 1 FROM dual WHERE 11 = any( ‘{‘11’,’22’,’22’}’::integer[]);关键字Oracle中一些常作为表名、列名、别名的单词,在GaussDB A中为保留关键字,不能作为表名、列名或别名。常见的有:Account/Source/NAME/VERSION/LABEL/POSITION不支持将关键字作为表名、列名或者别名。将关键字替换为非关键字。-INSERT INTO table_name向表中插入数据。Oracle支持使用别名。不支持使用别名。删除别名。Oracle:INSERT INTO test_a t (t.a,t.b) VALUES(3,4);GaussDB A:INSERT INTO test_a  (a,b) VALUES(3,4);select for update支持for uapdate 后面带子查询。不支持for uapdate 后面带子查询。修改SQL 语句先进行查询,后进行for update。Oracle:procedure lockglctrl (i_ctrltable     varchar2,                       i_ctrltemptable varchar2,                       i_ctrlbustable  varchar2) is    v_tmpsql varchar (2000);    begin    /*--锁控制表  如果业务备份表中有该单据记录那么也要把备份表中的来源锁住*/    v_tmpsql := 'select 1 from ' || i_ctrltable ||                ' t where exists  (select * from ' || i_ctrltemptable ||                ' a where a.fromctrlid = t.guid) for update';    execute immediate v_tmpsql;    v_tmpsql := 'select 1 from ' || i_ctrltable ||                ' t where exists  ( select 1 from  (select b.fromctrlid from ' ||                i_ctrlbustable || ' b where exists  (select 1 from ' ||                i_ctrltemptable ||                ' t1 where t1.guid = b.guid)) c where c.fromctrlid = t.guid) for update';    execute immediate v_tmpsql;end;GaussDB A:create or replace procedure lockglctrl(i_ctrltable     varchar2,                       i_ctrltemptable varchar2,                       i_ctrlbustable  varchar2) is      v_tmpsql varchar(2000);    v_tmpval varchar;      v_sql varchar;      v_str varchar;      v_num integer;    c1 refcursor;      c2 refcursor;    r1 record;      r2 record;  begin      v_tmpsql := 'select distinct t.guid from ' || i_ctrltable ||                ' t where exists (select * from ' || i_ctrltemptable ||                ' a where a.fromctrlid = t.guid)';    open c1 for execute v_tmpsql;      v_num := 0 ;      v_str := '';    loop        fetch c1 into r1;        exit when not found;            if v_num = 0 then                  v_str := '''' ||r1.guid||'''';                  v_num := v_num + 1;            else                  v_str := v_str || ',''' ||r1.guid||'''';                  v_num := v_num + 1;            end if;    end loop;      if v_str <> '' then            v_sql := 'select 1 from ' || i_ctrltable || ' t where t.guid in ( '|| v_str ||' ) for update ';            execute v_sql;      end if;      --dbms_output.put_line(v_sql);      --pg_sleep(10);            v_tmpsql := 'select distinct fromctrlid from (select b.fromctrlid from ' ||                i_ctrlbustable || ' b where exists (select 1 from ' ||                i_ctrltemptable ||                ' t1 where t1.guid = b.guid))';      dbms_output.put_line(v_tmpsql);    open c2 for execute v_tmpsql;      v_num := 0 ;    loop        fetch c2 into r2;        exit when not found;            if v_num = 0 then                  v_str := '''' ||r2.fromctrlid||'''';                  v_num := v_num + 1;            else                  v_str := v_str || ',''' ||r2.fromctrlid||'''';                  v_num := v_num + 1;            end if;    end loop;      if v_str <> '' then            v_sql := 'select 1 from ' || i_ctrltable || ' t where t.guid in ( ' || v_str || ' ) for update ';            --perform v_sql;            --dbms_output.put_line(v_sql);      end if;end;/
  • [迁移系列] Gauss DB(DWS)迁移系列-Oracle 函数转换差异(四)
    字符串处理函数:TO_NUMBER参数nlsparam:GaussDB A没有可选参数nlsparam。fmt:格式化字符串fmt和GaussDB A不完全一致,但是GaussDB A和PostgreSQL兼容。当需要将时间间隔转化为分钟数的时候,GaussDB A的to_number函数不能接受时间间隔类型的参数,可参考示例将to_number函数替换为cast函数来实现此功能。处理方案:在实施中如果TO_NUMBER带有nlsparam参数,建议直接删除参数。在实施中需要将时间间隔转化为分钟数的时候,将to_number函数替换为cast函数。示例OracleSELECT to_number ((to_date('201809091010s','yyyymmddhh24mis')-to_date('201809081111s','yyyymmddhh24mis'))*24*60 as number(16,2));GaussDB ASELECT cast((to_date('201809091010s','yyyymmddhh24mis')-to_date('201809081111s','yyyymmddhh24mis'))*24*60 as number(16,2));REPLACE函数说明GaussDB A中有类似函数replace(string  text, from text, to text),但是必须有第三个参数,没有需要用NULL替代。处理方案增加第三个参数,没有需要用NULL替代。示例OracleSELECT replace('1234','1') FROM dual; REPLACE('1234','1')-------------------234GaussDB ASELECT replace('1234','1',null) FROM dual; replace --------- 234(1 row)splitstr函数说明Oracle中的自建函数,用于行转列。GaussDB A不支持。处理方案使用GaussDB A的内置函数regexp_split_to_table;使用unnset和string_to_array的组合处理。示例OracleSELECT * FROM table (splitstr('11;22;33', ';')) ;GaussDB A方法一:使用unnset和string_to_array的组合处理。SELECT * FROM unnest(string_to_array('11;22;33',';')); unnest -------- 11 22 33(3 rows)方法二:使用内置函数regexp_split_to_table。SELECT * FROM regexp_split_to_table('11;22;33',';'); unnest -------- 11 22 33(3 rows)regexp_substr函数说明GaussDB A支持函数regexp_substr,语法和参数与Oracle有差异。Oracle数据库中支持五个参数:字符串,正则表达式,起始位置,匹配组,模式。GaussDB A数据库只支持两个参数:字符串,正则表达式。处理方案按照GaussDB A语法改写。regexp_replace函数说明GaussDB A支持函数regexp_replace。但是在Oracle中默认会进行全局替换,在GaussDB A中默认只会替换第一个满足条件的字符串。处理方案在Regexp_Replace函数中加上'g'参数,进行全局替换。regexp_instr函数说明正则表达式的抽取子串函数。GaussDB A无同名函数。处理方案使用regexp_substr函数替换regexp_instr函数。listagg函数说明GaussDB A无同名函数,可以用stringagg函数替换。处理方案使用stringagg函数替换Listagg函数。示例OracleSELECT listagg (T.OS_SOFTASSETS_ID,’,’) WITHIN GROUP (ORDER BY T.SOFTASSETS_ID);与窗口函数连用时:SELECT deptno,ename,sal,listagg(ename,',') WITHIN GROUP (order by sal) over (partition by deptno)name FROM emp;GaussDB ASELECT string_agg(T.OS_SOFTASSETS_ID,’,’);与窗口函数连用时:SELECT deptno,ename,sal,string_agg(ename,',')over(partition by deptno order by sal range between unbounded preceding and unbounded following) FROM emp;SYS_GUID函数说明SYS_GUID产生并返回一个全球唯一的标识符(原始值)由16个字节组成。GaussDB A目前无此语法,不兼容。处理方案目前没有很好的方法进行简单适配,只能自建函数实现此功能。自建函数示例如下:CREATE OR REPLACE FUNCTION public.sys_guid() RETURNS character varying LANGUAGE plpgsql FENCEDAS $function$ DECLARE  o_result varchar2; begin select nextval('sys_guid') into o_result from dual ;return o_result;end$function$current_user / user函数说明GaussDB ASQL语句中存在current_user或user写法时不能下推(V300R002C00之前版本),需要替换为可下推语法。处理方案在库中创建如下函数current_user_select(),并将业务中使用的current_user替换为current_user_select即可实现下推。函数current_user_select()如下:CREATE OR REPLACE FUNCTION public.current_user_select()RETURNS nameLANGUAGE internalimmutable STRICTAS $function$current_user$function$;示例OracleSELECT current_user FROM emp;GaussDB ASELECT current_user_select() FROM emp;sysdate/ current_date / SYSTIMESTAMP函数说明GaussDB ASQL语句中存在sysdate或current_date写法时不能下推(V300R002C00之前版本),同时,不支持SYSTIMESTAMP。处理方案需要使用current_timetamp(0)替换。current_timestamp带有时间及时区信息,如果不需要,可以使用substr函数截取。示例OracleSELECT sysdate FROM emp;SELECT current_date FROM emp;SELECT SYSTIMESTAMP FROM emp;GaussDB ASELECT current_timestamp(0) FROM emp;SELECT current_timestamp(0) FROM emp;SELECT current_timestamp(0) FROM emp;concat()函数说明GaussDB A SQL语句中如果存在concat函数,则整个语句不支持下推(6.5版本之前版本)。处理方案需要使用可改为||代替。示例OracleSELECT concat(first_name, last_name) FROM emp;GaussDB ASELECT first_name||last_name FROM emp;rowid函数说明是Oracle中的一个虚拟字段,用于返回特定行的具体地址。GaussDB A不支持。处理方案GaussDB A可用xc_node_id,tableoid,ctid三个虚拟字段的组合来替代。示例OracleSELECT empid, ename, ROWID FROM employees; GaussDB ASELECT empid, ename, CAST( ( xc_node_id || '#' || tableoid || '#' || ctid ) AS TEXT ) AS ROWID FROM employees; nvl2函数说明GaussDB A不支持。处理方案GaussDB A可用DECODE替代。示例OracleSELECT NVL2(Expr1, Expr2, Expr3) ;GaussDB ASELECT DECODE(Expr1, NULL, Expr3, Expr2) ;            keep函数说明keep是Oracle下的一个分析函数,它的用法不同于通过over关键字指定的分析函数,可以用于这样一种场合下:取同一个分组下以某个字段排序后,对指定字段取最小或最大的那个值。GaussDB A不支持。其实这个目标通过一般的dense_rank分析函数也可以实现,即指定rank=1。可结合示例修改keep函数。如果是取last,可以倒排序,如果取first,则可以正排序。处理方案GaussDB A可用dense_rank分析函数替代。指定rank=1。可结合下面例子修改keep函数。如果是取last,可以倒排序,如果取first,则可以正排序。示例OracleSELECT  max(id) keep(dense_rank last order by name) from test1 group by dept;GaussDB ASELECT max(v.id) FROM (select id,name, from test1) v WHERE dense_rank() OVER (partition by dept order by name desc) AS rankv.rank=1 GROUP BY v.dept;  
  • [迁移系列] Gauss DB(DWS)迁移系列-Oracle 函数转换差异(二)
    date 处理函数ADD_MONTHSGaussDB A目前无此语法,不兼容。处理方案现场实施的可以采取 + interval '1 month' 的方式实现同样功能。MONTHS_BETWEENGaussDB A目前无此语法,不兼容。处理方案目前没有很好的方法进行简单适配,只能自建函数实现此功能。自建函数示例如下:CREATE OR REPLACE FUNCTION MONTHS_BETWEEN(date, date) RETURNS double precision    AS $$    SELECT   CASE    --WHEN EXTRACT(day from $1) = EXTRACT(day FROM $2) THEN   /* the two dates have the same day component */       -- EXTRACT(year FROM $1) * 12 + EXTRACT(month FROM $1) - EXTRACT(year FROM $2) * 12 - EXTRACT(month FROM $2)    WHEN (EXTRACT(day FROM $1) = EXTRACT(day FROM (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day'))) and    (EXTRACT(day FROM $2) = EXTRACT(day FROM (date_trunc('MONTH', $2) + INTERVAL '1 MONTH - 1 day'))) THEN /* both the last day of the month */        EXTRACT(year FROM $1) * 12 + EXTRACT(month FROM $1) - EXTRACT(year FROM $2) * 12 - EXTRACT(month FROM $2)    when (EXTRACT(day FROM $1) = EXTRACT(day FROM (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day'))) and    (EXTRACT(day FROM $2) <> EXTRACT(day FROM (date_trunc('MONTH', $2) + INTERVAL '1 MONTH - 1 day'))) then         (EXTRACT(year FROM $1)-EXTRACT(year FROM $2))*12+         EXTRACT(month FROM $1)-EXTRACT(month FROM $2)+1-EXTRACT(day FROM $2)/31    when (EXTRACT(day FROM $1) <> EXTRACT(day FROM (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day'))) and    (EXTRACT(day FROM $2) = EXTRACT(day FROM (date_trunc('MONTH', $2) + INTERVAL '1 MONTH - 1 day'))) then         (EXTRACT(year FROM $1)-EXTRACT(year FROM $2))*12+         EXTRACT(month FROM $1)-EXTRACT(month FROM $2)-1+ EXTRACT(day FROM $1)/31         ELSE        (EXTRACT(year FROM $1)-EXTRACT(year FROM $2))*12+         EXTRACT(month FROM $1)-EXTRACT(month FROM $2)+         (EXTRACT(day FROM $1)-EXTRACT(day FROM $2))/31END NEXT_DAYGaussDB A目前无此语法,不兼容。处理方案实施中遇到此问题可以用 + interval '1 day'的方式简单适配。TRUNC(date)参数GaussDB A有一个类似的函数date_trunc(text, timestamp)。fmt:Oracle的fmt是可选的,GaussDB A的fmt是必选的。输出无差异。处理方案实施中此函数可以用GaussDB A的date_trunc实现。ROUND(date)GaussDB A目前无此语法,不兼容。处理方案:目前没有好的适配方案,只能自定义函数的形式实现逻辑。
  • [迁移系列] Gauss DB(DWS)迁移系列-Oracle 函数转换差异(一)
    与Oracle函数的差异TO_DATE参数fmt:有些Oracle的fmt格式GaussDB A没有,但是GaussDB A和Oracle的fmt绝大部分是一致的。GaussDB A的fmt是和PostgreSQL兼容的。nlsparam:GaussDB A没有可选参数nlsparam。此处的nlsparam是指参数NLS_DATE_LANGUAGE,主要是指结果的显示语言。输出Oracle输出格式为Date类型。GaussDB A输出格式为Timestamp类型。处理方案在实施中如果to_date带有nlsparam参数,建议直接删除参数。TO_CHAR(datetime)参数fmt:有些Oracle的fmt格式GaussDB A没有,但是GaussDB A和Oracle的fmt绝大部分是一致的。GaussDB A和PostgreSQL支持的fmt格式兼容。nlsparam:GaussDB A没有可选参数nlsparam,此处的nlsparam是指参数NLS_DATE_LANGUAGE,主要是指结果的显示语言。输出无差异。处理方案在实施中如果to_char带有nlsparam参数,建议直接删除参数。TO_CHAR (character)GaussDB A目前无此语法,不兼容。TO_CHAR (number)参数fmt:格式中B C PR TM U X,GaussDB A不支持。但是这几种格式相对生僻。nlsparam:GaussDB A没有可选参数nlsparam,此处的nlsparam是指NLS_NUMERIC_CHARACTERS,NLS_CURRENCY,NLS_ISO_CURRENCY。功能近似于对应GaussDB A的lc_monetary,lc_numeric。输出无差异。处理方案在实施中如果to_char带有nlsparam参数,建议直接删除参数。
  • [迁移系列] Gauss DB(DWS)迁移系列-Oracle 数据类型差异
    Oracle数据类型差异数据类型OracleGaussDB A处理方案NUMBER支持两种类型:l  NUMBER(10,0)l  NUMBER(10.0)只支持NUMBER(10,0)格式。替换SMALLINT支持自定义精度,例如SMALLINT(10,0)。不支持自定义精度,如SMALLINT。替换VARCHAR2支持类型:VARCHAR2( num char)支持类型:NVARCHAR2(n)替换
  • [迁移系列] [项目实践-迁移系列] Gauss DB(DWS)迁移系列-Oracle迁移分析函数
    Oracle 的分析函数ratio_to_report函数名称OracleGaussDB A迁移方案ratio_to_reportselect    name,    score,    ratio_to_report(score) over() as  "占所有科目的百分比",    ratio_to_report(score) over(partition by kemu) as  "占各科目的百分比"from student_info;select    name,    score,    (score/sum(score) over()) as "占所有科目的百分比",    (score/sum(score) over(partition by kemu)) as "占各科目的百分比"from student_info;
  • [迁移系列] Oracle + APEX 迁移至 DWS + 帆软
    迁移背景:从Oracle + apex 转向 DWS + 帆软其中apex通过匿名块拼接SQL语句,再执行语句获得查询结果帆软不支持类似于APEX的"两阶段"执行方式,仅支持一次性返回报表结果解决方法:将匿名块跟执行SQL封装在一个function中,上层直接调用对应的SQL语句获取对应的报表的结果。客户侧从备份文件中获取PLSQL语句,格式较为统一,考虑转换function脚本自动化。脚本设计:(1)APEX绑定参数均为[:P]开头,脚本可识别为入参。(2)function需要定义出参类型,目前没想到一劳永逸的返回任意类型方案。但DWS在执行function时如果出参个数及其类型与实际执行结果不符时会报错,可以利用这一点进行function的修改。脚本输出:(1)通过declare及end识别匿名块(1)识别匿名块的绑定参数,生成function的定义,入参确定,出参使用returns table,初始化状态仅指定一个出参,类型默认为text。(2)调用函数,出参默认使用null值,此时DWS存在三类报错a.function定义的出参个数与function实际的返回字段数不符:函数定义新增一个字段,默认类型还是text类型,直至无此报错b.function定义的出参类型与function实际的返回字段类型不符:修改对应出参类型为正确的类型,直至无此报错c.其他语法等报错:跳过该匿名的转换,后续手工修改语法问题:1、数字开头的对象名需要用双引号包围,否则会语法报错postgres=# create table test("48小时出货量" int)distribute by hash("48小时出货量"); CREATE TABLE postgres=# \d+ test; Table "public.test" Column | Type | Modifiers | Storage | Stats target | Description --------------+---------+-----------+---------+--------------+------------- 48小时出货量 | integer | | plain | | Has OIDs: no Distribute By: HASH(48小时出货量) Location Nodes: ALL DATANODES Options: orientation=row, compression=no postgres=# select "48小时出货量" from test; 48小时出货量 -------------- (0 rows)2、双引号可以使得对象名保持大写,Oracle默认对象名大写,DWS默认小写,如果有双引号DWS查询时也需要双引号+大写postgres=# create table test(ID int, GRADE int, "SCORE" int)distribute by hash(ID); CREATE TABLE postgres=# \d+ test; Table "public.test" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------+---------+--------------+------------- id | integer | | plain | | grade | integer | | plain | | SCORE | integer | | plain | | Has OIDs: no Distribute By: HASH(id) Location Nodes: ALL DATANODES Options: orientation=row, compression=no postgres=# select id,GRADE ,"SCORE" from test; id | grade | SCORE ----+-------+------- (0 rows)3、DECODE/CASE WHEN表达式的不同结果需要类型一致postgres=# select to_char(日期,'yyyy-mm-dd') 日期,DECODE(sign("时段"-10),-1,'0'||"时段","时段") 时段 from APEX_ACTIVE_USER_H; ERROR: CASE types numeric and text cannot be matched LINE 1: ...日期,'yyyy-mm-dd') 日期,DECODE(sign("时段"-10),-1,'0'||"时段... ^ CONTEXT: referenced column: 时段 postgres=# select to_char(日期,'yyyy-mm-dd') 日期,DECODE(sign("时段"-10),-1,'0'||"时段","时段"::text) 时段 from APEX_ACTIVE_USER_H; 日期 | 时段 ------+------ (0 rows) postgres=# select to_char(日期,'yyyy-mm-dd') 日期,DECODE(sign("时段"-10),-1,('0'||"时段")::numeric,"时段") 时段 from APEX_ACTIVE_USER_H; 日期 | 时段 ------+------ (0 rows) postgres=# \d+ APEX_ACTIVE_USER_H Table "public.apex_active_user_h" Column | Type | Modifiers | Storage | Stats target | Description ----------+--------------------------------+-----------+----------+--------------+------------- id | numeric | not null | main | | 日期 | timestamp(0) without time zone | | plain | | 时段 | numeric | | main | | 渗透类型 | character varying(100) | | extended | | 活跃人数 | numeric | | main | | Indexes: "apex_active_user_h_pkey" PRIMARY KEY, btree (id) TABLESPACE pg_default Has OIDs: no Distribute By: HASH(id) Location Nodes: ALL DATANODES Options: orientation=row, compression=no4、针对date类型的trunc函数需要使用date_trunc替代,因为性能问题不建议使用自定义trunc函数PS:date_trunc('YEAR/QUARTER/MONTH/WEAK/DAY',date),第二个入参必须是时间类型,如果为字符类型必须先转换为时间类型postgres=# \df date_trunc List of functions Schema | Name | Result data type | Argument data types | Type | fencedmode | propackage ------------+------------+-----------------------------+-----------------------------------+--------+------------+------------ pg_catalog | date_trunc | interval | text, interval | normal | f | f pg_catalog | date_trunc | timestamp without time zone | text, timestamp without time zone | normal | f | f pg_catalog | date_trunc | timestamp with time zone | text, timestamp with time zone | normal | f | f (3 rows)5、function如果直接select null,返回类型为text6、匿名块的设计可能存在多分支多语句,不同语句其返回字段的类型可能不同,此时需要加强制类型转换确保不同SQL返回字段类型相同PS:上层报表的字段数量是一定的,所以返回结果集字段的数量不会有变化7、pivot可通过decode/case when语句进行改写,decode更简洁,推荐使用。可视情况使用group by / group by rollup-- pivot行转列 使用sum + group by [rollup] 输出分组列名不存在的行即可 select * from ( select "时段", "订单数量" from APEX_ORDER_ANALYSIS_HB_HOUR where "下单日期" =to_date('2021-04-05','yyyy-mm-dd')) pivot (sum("订单数量") for "时段" in (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23)); select "0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23" from( select 时段, sum(decode(时段,0 ,订单数量)) as "0", sum(decode(时段,1 ,订单数量)) as "1", sum(decode(时段,2 ,订单数量)) as "2", sum(decode(时段,3 ,订单数量)) as "3", sum(decode(时段,4 ,订单数量)) as "4", sum(decode(时段,5 ,订单数量)) as "5", sum(decode(时段,6 ,订单数量)) as "6", sum(decode(时段,7 ,订单数量)) as "7", sum(decode(时段,8 ,订单数量)) as "8", sum(decode(时段,9 ,订单数量)) as "9", sum(decode(时段,10,订单数量)) as "10", sum(decode(时段,11,订单数量)) as "11", sum(decode(时段,12,订单数量)) as "12", sum(decode(时段,13,订单数量)) as "13", sum(decode(时段,14,订单数量)) as "14", sum(decode(时段,15,订单数量)) as "15", sum(decode(时段,16,订单数量)) as "16", sum(decode(时段,17,订单数量)) as "17", sum(decode(时段,18,订单数量)) as "18", sum(decode(时段,19,订单数量)) as "19", sum(decode(时段,20,订单数量)) as "20", sum(decode(时段,21,订单数量)) as "21", sum(decode(时段,22,订单数量)) as "22", sum(decode(时段,22,订单数量)) as "23" from APEX_ORDER_ANALYSIS_HB_HOUR where 下单日期 =to_date('2021-04-05','yyyy-mm-dd') group by rollup(时段) )where 时段 is null; select "0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23" from( select total, sum("0")as "0", sum("1")as "1", sum("2")as "2", sum("3")as "3", sum("4")as "4",sum("5")as "5",sum("6")as "6", sum("7")as "7", sum("8")as "8", sum("9")as "9", sum("10")as "10", sum("11")as "11",sum("12")as "12", sum("13")as "13", sum("14")as "14",sum("15")as "15",sum("16")as "16",sum("17")as "17",sum("18")as "18", sum("19")as "19",sum("20")as "20",sum("21")as "21",sum("22")as "22",sum("23")as "23" from( select 1 as total,时段, sum(decode(时段,0 ,订单数量)) as "0", sum(decode(时段,1 ,订单数量)) as "1", sum(decode(时段,2 ,订单数量)) as "2", sum(decode(时段,3 ,订单数量)) as "3", sum(decode(时段,4 ,订单数量)) as "4", sum(decode(时段,5 ,订单数量)) as "5", sum(decode(时段,6 ,订单数量)) as "6", sum(decode(时段,7 ,订单数量)) as "7", sum(decode(时段,8 ,订单数量)) as "8", sum(decode(时段,9 ,订单数量)) as "9", sum(decode(时段,10,订单数量)) as "10", sum(decode(时段,11,订单数量)) as "11", sum(decode(时段,12,订单数量)) as "12", sum(decode(时段,13,订单数量)) as "13", sum(decode(时段,14,订单数量)) as "14", sum(decode(时段,15,订单数量)) as "15", sum(decode(时段,16,订单数量)) as "16", sum(decode(时段,17,订单数量)) as "17", sum(decode(时段,18,订单数量)) as "18", sum(decode(时段,19,订单数量)) as "19", sum(decode(时段,20,订单数量)) as "20", sum(decode(时段,21,订单数量)) as "21", sum(decode(时段,22,订单数量)) as "22", sum(decode(时段,22,订单数量)) as "23" from APEX_ORDER_ANALYSIS_HB_HOUR where 下单日期 =to_date('2021-04-05','yyyy-mm-dd') group by 时段 )group by total);8、unpivot可通过unnest实现--Oracle select * from ( select 商品销售额, 预收运费金额, 运费优惠券抵扣金额, 商品销售额+预收运费金额-运费优惠券抵扣金额 应收金额, 微信支付金额, 支付宝支付金额, 余额支付金额, 其他支付金额,一手币支付金额 from ( select round(sum(商品销售额),2) as 商品销售额 ,round(sum(case when 支付方式 != 9 and 是否包邮 = 0 then 预收运费金额 else 0 end)-sum(部分退款补扣运费),2) as 预收运费金额 ,round(sum(运费优惠券抵扣金额),2) as 运费优惠券抵扣金额 ,round(sum(微信支付金额),2) as 微信支付金额 ,round(sum(支付宝支付金额),2) as 支付宝支付金额 ,round(sum(余额支付金额),2) as 余额支付金额 ,round(sum(其他支付金额)-sum(case when 支付方式 = 9 and 是否包邮 = 0 then 预收运费金额 else 0 end),2) as 其他支付金额 ,round(sum(一手币支付金额),2) 一手币支付金额 from APEX_CW_SALE_ORDER_W@SLAVE12C_6 where 订单日期 between to_date('2021-04-05','yyyy-mm-dd') and to_date('2021-04-08,'yyyy-mm-dd') ) a ) a unpivot (指标名称 for 指标值 in (商品销售额, 预收运费金额, 运费优惠券抵扣金额, 应收金额, 微信支付金额, 支付宝支付金额, 余额支付金额, 其他支付金额,一手币支付金额)) ; --DWS select unnest(ARRAY['商品销售额', '预收运费金额', '运费优惠券抵扣金额','应收金额','微信支付金额','支付宝支付金额','余额支付金额','其他支付金额','一手币支付金额']) AS 指标名称, unnest(ARRAY[商品销售额,预收运费金额,运费优惠券抵扣金额,应收金额,微信支付金额,支付宝支付金额,余额支付金额,其他支付金额,一手币支付金额]) AS 指标值 from( select 商品销售额, 预收运费金额, 运费优惠券抵扣金额, 商品销售额+预收运费金额-运费优惠券抵扣金额 应收金额, 微信支付金额, 支付宝支付金额, 余额支付金额, 其他支付金额,一手币支付金额 from ( select round(sum(商品销售额),2) as 商品销售额 ,round(sum(case when 支付方式 != 9 and 是否包邮 = 0 then 预收运费金额 else 0 end)-sum(部分退款补扣运费),2) as 预收运费金额 ,round(sum(运费优惠券抵扣金额),2) as 运费优惠券抵扣金额 ,round(sum(微信支付金额),2) as 微信支付金额 ,round(sum(支付宝支付金额),2) as 支付宝支付金额 ,round(sum(余额支付金额),2) as 余额支付金额 ,round(sum(其他支付金额)-sum(case when 支付方式 = 9 and 是否包邮 = 0 then 预收运费金额 else 0 end),2) as 其他支付金额 ,round(sum(一手币支付金额),2) 一手币支付金额 from APEX_CW_SALE_ORDER_W where 订单日期 between to_date('2021-04-05','yyyy-mm-dd') and to_date('2021-04-08','yyyy-mm-dd') ));9、Oracle自增主键可使用function+trigger的形式替代(update:考虑性能影响,DWS建议使用sequence+primary key)-- Oracle create sequence MY_SUPPLY_CG_KEEP_M_SEQ minvalue 1 maxvalue 9999999999999999999999999999 start with 49421 increment by 1 cache 20; CREATE OR REPLACE TRIGGER "MY_SUPPLY_CG_KEEP_M_TRIG" before insert on "SUPPLY_CG_KEEP_M" for each row begin if :new."ID" is null then select "MY_SUPPLY_CG_KEEP_M_SEQ".nextval into :new."ID" from sys.dual; end if; end; / --DWS create sequence MY_SUPPLY_CG_KEEP_M_SEQ --创建序列 minvalue 1 maxvalue 9223372036854775807 --注意Oracle与DWS序列的最大值范围不同,需要修改 start with 2757641 increment by 1 cache 20;--DWS仅支持cache关键字,详见DWS产品文档 CREATE OR REPLACE FUNCTION MY_SUPPLY_CG_KEEP_M_TRIG_FUNC() RETURNS trigger AS $BODY$ --创建函数 BEGIN NEW.ID := nextval('MY_SUPPLY_CG_KEEP_M_SEQ'); RETURN NEW; END; $BODY$ LANGUAGE 'plpgsql'; CREATE TRIGGER MY_SUPPLY_CG_KEEP_M_TRIG --创建触发器 BEFORE INSERT ON SUPPLY_CG_KEEP_M FOR EACH ROW WHEN (NEW.ID IS NULL) EXECUTE PROCEDURE MY_SUPPLY_CG_KEEP_M_TRIG_FUNC(); ALTER SEQUENCE MY_SUPPLY_CG_KEEP_M_SEQ OWNED BY SUPPLY_CG_KEEP_M.ID; --添加关联10、in  nvl() 修改为 = nvl()11、Navitcat在执行大结果集语句时性能较差,建议使用Data Studio12、array的初始化在DWS的新老版本上有差异,老版本需要使用valname = array[];的形式进行初始化,新版本可以使用valname=valname();形式进行初始化13、case when表达式不同分支返回的类型可能不一致,转换存储过程后如果类型不一致需要对输出列做类型强转
  • [迁移系列] ORACLE 触发器在GaussDB(DWS)上的实现
    触发器ORACLE跟GaussDB(DWS)有一定区别,在项目实践的时候踩了些坑,在下面博客记录一下https://bbs.huaweicloud.com/blogs/196527
  • [迁移系列] oracle CONNECT_BY_ROOT,CONNECT_BY_ISLEAF等在GaussDB(DWS)实现
    oracle的connect_by_root,connect_by_isleaf 在gaussdb(DWS) 没有直接的函数,以下博客展示如何实现这两个函数。https://bbs.huaweicloud.com/blogs/196094
  • [SQL] GaussDB(DWS) TD和Oracle兼容模式的差异
    【摘要】 GaussDB(DWS) TD和Oracle两种兼容模式的差异,以及对每种差异做了举例说明。GaussDB(DWS)支持两种兼容模式,即TD(Teradata)兼容模式、ORA(Oracle)兼容模式。可以在CREATE DATABASE时通过指定选项DBCOMPATIBILITY进行选择。语法如下:--创建兼容TD的数据库 postgres=# CREATE DATABASE td_compatible_db DBCOMPATIBILITY 'TD';CREATE DATABASE--创建兼容ORA的数据库 postgres=# CREATE DATABASE ora_compatible_db DBCOMPATIBILITY 'ORA';CREATE DATABASEpostgres=# SELECT datname,datcompatibility FROM PG_DATABASE WHERE datname LIKE '%compatible_db';       datname      | datcompatibility  -------------------+------------------  td_compatible_db  | TD  ora_compatible_db | ORA(2 rows)两种兼容模式的注意差异对比如下表格所示:下面对每一个兼容性进行举例说明:-- 切到TD兼容的库下,创建表并插入数据 postgres=# \c td_compatible_db td_compatible_db=# CREATE TABLE td_table(a INT,b VARCHAR(5),c date);NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'a' as the distribution column by default.HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.CREATE TABLEtd_compatible_db=# INSERT INTO td_table VALUES(1,null,CURRENT_DATE);   INSERT 0 1td_compatible_db=# INSERT INTO td_table VALUES(2,'',CURRENT_DATE);  INSERT 0 1-- 区分空串和NULL,date类型只显示年月日td_compatible_db=# SELECT a, b, b IS NULL AS null, c FROM td_table;  a | b | null |     c       ---+---+------+------------  1 |   | t    | 2020-06-19  2 |   | f    | 2020-06-19(2 rows)td_compatible_db=# SELECT CURRENT_DATE;     date     ------------  2020-06-19(1 row)-- 空串转int,转为0。TD数据库不同于Oracle,Oracle将空串当做NULL进行处理,TD在将空串转换为数值类型的时候,默认将空串转换为0进行处理,因此查询空串会查询到数值为0的数据。同样地,在TD兼容模式下,字符串转换数值的过程中,也会将空串默认转换为相应数值类型的0值进行处理。除此之外,' - '、' + '、' '这些字符串也都会在TD兼容模式下默认转换为0进行处理,但是小数点字符串' . '会报错td_compatible_db=# SELECT b::int FROM td_table WHERE b = '';     b  --- 0(1 row)--超长字符自动截断。当连接到TD兼容的数据库时,td_compatible_truncation参数设置为on时,将启用超长字符串自动截断功能,在后续的insert语句中(不包含外表的场景下),对目标表中char和varchar类型的列上插入超长字符串时,系统会自动按照目标表中相应列定义的最大长度对超长字符串进行截断。td_compatible_db=# SHOW td_compatible_truncation;  td_compatible_truncation  -------------------------- on(1 row)td_compatible_db=# INSERT INTO td_table VALUES(3,'12345678',CURRENT_DATE);INSERT 0 1td_compatible_db=# SELECT * FROM td_table WHERE a = 3;                      a |   b   |     c       ---+-------+------------  3 | 12345 | 2020-06-19(1 row)--varchar   + int运算,转为numeric + numeric计算td_compatible_db=# EXPLAIN VERBOSE SELECT b + a FROM td_table WHERE a = 3;                                           QUERY PLAN                                           ----------------------------------------------------------------------------------------------- Data Node Scan  (cost=0.00..0.00 rows=0 width=0)    Output: (((td_table.b)::numeric + (td_table.a)::numeric))    Node/s: datanode7    Remote query: SELECT b::numeric + a::numeric AS "?column?" FROM public.td_table WHERE a = 3(4 rows)-- case和coalesce表达式。对于case 和coalesce,在TD 兼容模式下的处理● 如果所有输入都是相同的类型,并且不是unknown类型,那么解析成这种类型。● 如果所有输入都是unknown类型则解析成text类型。● 如果输入字符串(包括unknown,unknown当text来处理)和数字类型,那么解析成字符串类型,如果是其他不同的类型范畴,则报错。● 如果输入类型是同一个类型范畴,则选择该类型的优先级较高的类型。● 把所有输入转换为所选的类型。如果从给定的输入到所选的类型没有隐式转换则失败。示例1:Union中的待定类型解析。这里,unknown类型文本'b'将被解析成text类型。td_compatible_db=# SELECT text 'a' AS "text" UNION SELECT 'b';text------ab(2 rows)示例2:简单Union中的类型解析。文本1.2的类型为numeric,而且integer类型的1可以隐含地转换为numeric,因此使用这个类型。td_compatible_db=# SELECT 1.2 AS "numeric" UNION SELECT 1;numeric---------11.2(2 rows)示例3:转置Union中的类型解析。这里,因为类型real不能被隐含转换成integer,但是integer可以隐含转换成real,那么联合的结果类型将是real。td_compatible_db=# SELECT 1 AS "real" UNION SELECT CAST('2.2' AS REAL);real------12.2(2 rows)示例4:TD模式下,coalesce参数输入int和varchar类型,那么解析成varchar类型。ORA模式下会报错。查看coalesce参数输入int和varchar类型的查询语句的执行计划如下td_compatible_db=# EXPLAIN VERBOSE select coalesce(a, b) FROM td_table;                                          QUERY PLAN                                          --------------------------------------------------------------------------------------------- Data Node Scan  (cost=0.00..0.00 rows=0 width=0)    Output: (COALESCE((td_table.a)::character varying, td_table.b))    Node/s: All datanodes    Remote query: SELECT COALESCE(a::character varying, b) AS "coalesce" FROM public.td_table(4 rows)原文链接:https://bbs.huaweicloud.com/blogs/176361【推荐阅读】【最新活动汇总】DWS活动火热进行中,互动好礼送不停(持续更新中)  HOT  【博文汇总】GaussDB(DWS)博文汇总1,欢迎大家交流探讨~(持续更新中)【维护宝典汇总】GaussDB(DWS)维护宝典汇总贴1,欢迎大家交流探讨(持续更新中)【项目实践汇总】GaussDB(DWS)项目实践汇总贴,欢迎大家交流探讨(持续更新中)【DevRun直播汇总】GaussDB(DWS)黑科技直播汇总,欢迎大家交流学习(持续更新中)【培训视频汇总】GaussDB(DWS) 培训视频汇总,欢迎大家交流学习(持续更新中)扫码关注我哦,我在这里↓↓↓
  • [迁移系列] 【Oracle语法迁移】Rownum语法迁移案例
    建表:create table tb_recursive(id int,parent_id int);1.select list中Oracle中实现方式:select id,parent_id,rownum from tb_recursive order by 3;DWS中实现方式:select id,parent_id,row_number() over() from tb_recursive order by 3;2.where条件中Oracle中实现方式:select id,parent_id from tb_recursive where rownum =1 order by id;DWS中实现方式:select id,parent_id from tb_recursive order by id limit 1;3.sql语句中有order byOracle中实现方式:select id from (select id,parent_id from tb_recursive order by id desc) where rownum =1;DWS中实现方式:select id from tb_recursive order by id desc limit 1;4.sql语句中有countOracle中实现方式:select count(1) from (select id,parent_id from tb_recursive where id = 1) where rownum =1;DWS中实现方式:select count(1) from (select id,parent_id from tb_recursive where id = 1 limit 1);
  • [迁移系列] 【Oracle语法迁移】connect_by_root语法迁移案例
    建表:create table tb_recursive(id int,parent_id int);Oracle中实现方式:select id,parent_id,connect_by_root(parent_id) as rid,level lvl from tb_recursive connect by prior id=parent_id start with parent_id =2 order by id;DWS中实现方式:with recursive r1 as (select id,parent_id,parent_id rid,1 as lvlfrom tb_recursivewhere parent_id =2union allselect t.id,t.parent_id,t1.rid,t1.lvl+1 as lvlfrom tb_recursive t,r1 t1where t.parent_id=t1.id)select id,parent_id,rid,lvl from r1 order by id;
  • [迁移系列] 【Oracle语法迁移】order siblings by语法迁移案例
    建表:create table tb_recursive(id int,parent_id int);Oracle中实现方式:select id,parent_id,level lvl from tb_recursive connect by prior id=parent_id start with parent_id is null order siblings by id;DWS中实现方式:with recursive r1 as (select id,parent_id,1 as lvl,lpad(id,4,0)||lpad(ctid||xc_node_id::text,20,0) sortnofrom tb_recursivewhere parent_id is nullunion allselect t.id,t.parent_id,t1.lvl+1 as lvl,t1.sortno||lpad(t.id,4,0)||lpad(ctid||xc_node_id::text,20,0) sortnofrom tb_recursive t,r1 t1where t.parent_id=t1.id)select id,parent_id,lvl from r1 order by sortno;
  • [迁移系列] 【Oracle语法迁移】sys_connect_by_path语法迁移案例
    建表:create table tb_recursive(id int,parent_id int);Oracle写法:select id,parent_id,sys_connect_by_path(id,' > ') path,level lvl from tb_recursive connect by prior id= parent_id start with parent_id is null order by id;DWS改写如下:with recursive r1 as (select id,parent_id,' > '||id path,1 as lvlfrom tb_recursivewhere parent_id is nullunion allselect t.id,t.parent_id,t1.path||' > '||t.id path,t1.lvl+1 as lvlfrom tb_recursive t,r1 t1where t.parent_id=t1.id )select id,parent_id,path,lvl from r1 order by id;
总条数:180 到第
上滑加载中