我甚至不知道是否可能 - 所以任何想法都会受到欢迎:
我想要一个函数,它将返回一个字符串(varchar2
)表示2个特定表的列值的差异。
所以它的任务将是
找到属于2个表的2行之间的差异(会发生这种情况 具有相同的结构)。
请考虑以下情况。
表A(A_rowid,col1,col2,col3,col4,col5 ...,coln)和值(id1,val1,val2,val3,..,valn)
表B(B_rowid,col1,col2,col3,col4,col5 ...,coln)和值(id2,val1,val2',val3,..,valn')
* A_rowid - tableA的唯一键,B_rowid - 表B的唯一键
fnction diff(A_rowid number, B_rowid number) returns varchar2 is
begin
--do something
end;
表格的所有列都可以视为Varchar2 。
因此,
预期输出 - >
如果没有找到差异,空
或
diff:col2:val2-> val2',coln:valn-> valn'
重要的是,我希望在没有硬编码列名的情况下
(虽然表名是硬编码的)。
e.g。如果我们在表中添加其他列 - 函数应该仍然有效。
答案 0 :(得分:2)
你可以使用这个:
FUNCTION diff(A_rowid NUMBER, B_rowid NUMBER) RETURN VARCHAR2 IS
CURSOR TabColumns IS
SELECT COLUMN_NAME, COLUMN_ID
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'TABLE_A'
AND COLUMN_NAME <> 'A_ROWID'
ORDER BY COLUMN_ID;
sqlstr VARCHAR2(1000);
val_a VARCHAR2(4000);
val_b VARCHAR2(4000);
res VARCHAR2(30000);
BEGIN
FOR aCol IN TabColumns LOOP
BEGIN
sqlstr := 'SELECT a.'||aCol.COLUMN_NAME||', b.'||aCol.COLUMN_NAME;
sqlstr := sqlstr ||' FROM TABLE_A a CROSS JOIN TABLE_B b ';
sqlstr := sqlstr || ' WHERE A_rowid = :aRow AND B_rowid = :bRow ';
sqlstr := sqlstr || ' AND LNNVL(a.'||aCol.COLUMN_NAME||' = b.'||aCol.COLUMN_NAME||') ';
sqlstr := sqlstr || ' AND COALESCE(a.'||aCol.COLUMN_NAME||', b.'||aCol.COLUMN_NAME||') IS NOT NULL ';
EXECUTE IMMEDIATE sqlstr INTO val_a, val_b USING A_rowid, B_rowid;
res := res ||', '||aCol.COLUMN_NAME||':'||val_a||'->'||val_b;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
END LOOP;
RETURN REGEXP_REPLACE(res, '^, ', 'diff:');
END;
注意,如果是NULL值,则需要函数LNNVL(a.'||aCol.COLUMN_NAME||' = b.'||aCol.COLUMN_NAME||')
。
当其中一个值为NULL时,条件a.COLUMN_NAME <> b.COLUMN_NAME
会返回任何内容。
LNNVL(a.COLUMN_NAME = b.COLUMN_NAME)
相当于
( a.COLUMN_NAME <> b.COLUMN_NAME
OR (a.COLUMN_NAME IS NULL AND b.COLUMN_NAME IS NOT NULL)
OR (a.COLUMN_NAME IS NOT NULL AND b.COLUMN_NAME IS NULL) )
但是,只有在不关心性能时才使用上述功能。更高级的解决方案是这个:
FUNCTION diff(A_rowid NUMBER, B_rowid NUMBER) RETURN VARCHAR2 IS
CURSOR TabColumns IS
SELECT COLUMN_NAME, COLUMN_ID
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'TABLE_A'
AND COLUMN_NAME <> 'A_ROWID'
ORDER BY COLUMN_ID;
sqlstr VARCHAR2(10000);
val_a VARCHAR2(4000);
val_b VARCHAR2(4000);
res VARCHAR2(30000);
cur INTEGER;
p INTEGER;
res INTEGER;
BEGIN
sqlstr := 'SELECT '
FOR aCol IN TabColumns LOOP
sqlstr := ' a.'||aCol.COLUMN_NAME||'_A, b.'||aCol.COLUMN_NAME||'_B, ';
END LOOP;
sqlstr := REGEXP_REPLACE(sqlstr, ', $', ' FROM TABLE_A a CROSS JOIN TABLE_B b ');
sqlstr := sqlstr || ' WHERE A_rowid = :aRow AND B_rowid = :bRow ';
cur := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cur, sqlStr, DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE (cur, ':aRow', A_rowid);
DBMS_SQL.BIND_VARIABLE (cur, ':bRow', B_rowid);
p := 1;
FOR aCol IN TabColumns LOOP
DBMS_SQL.DEFINE_COLUMN(cur, p, aCol.COLUMN_NAME||'_A', 4000);
DBMS_SQL.DEFINE_COLUMN(cur, p+1, aCol.COLUMN_NAME||'_B', 4000);
p := p + 2;
END LOOP;
res := DBMS_SQL.EXECUTE_AND_FETCH(cur, TRUE);
p := 1;
FOR aCol IN TabColumns LOOP
DBMS_SQL.COLUMN_VALUE(cur, p, val_a);
DBMS_SQL.COLUMN_VALUE(cur, p+1, val_b);
p := p + 2;
IF val_a <> val_b OR (val_a IS NULL AND val_b IS NOT NULL) OR (val_a IS NOT NULL AND val_b IS NULL) THEN
res := res ||', '||aCol.COLUMN_NAME||':'||val_a||'->'||val_b;
END IF;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(cur);
RETURN REGEXP_REPLACE(res, '^, ', 'diff:');
END;
答案 1 :(得分:1)
尝试此功能
FUNCTION getdiff(arowid varchar, browid varchar) RETURN CLOB IS
v_line clob;
v_col_cnt INTEGER;
v_ind NUMBER;
rec_tab dbms_sql.desc_tab;
v_cursor NUMBER;
v_sql clob;
V_FIRST clob;V_SECOND CLOB;
V_FINAL CLOB;
begin
V_SQL := Q'$ select * from(select * from Table1 where rowid=:arowid)a,
(select * from Table2 where rowid=:browid)b $';
V_CURSOR := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(V_CURSOR, V_SQL, DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE (V_CURSOR, ':arowid', arowid);
DBMS_SQL.BIND_VARIABLE (V_CURSOR, ':browid', browid);
DBMS_SQL.DESCRIBE_COLUMNS(V_CURSOR, V_COL_CNT, REC_TAB);
FOR V_POS IN 1 .. REC_TAB.LAST LOOP
V_LINE := REC_TAB(V_POS).COL_NAME;
DBMS_SQL.DEFINE_COLUMN(V_CURSOR, V_POS, V_LINE);
END LOOP;
V_IND := DBMS_SQL.EXECUTE(V_CURSOR);
LOOP
V_IND := DBMS_SQL.FETCH_ROWS(V_CURSOR);
EXIT WHEN V_IND = 0;
FOR V_COL_SEQ IN 1 .. REC_TAB.COUNT LOOP
if v_col_seq <=V_COL_CNT/2 then
DBMS_SQL.COLUMN_VALUE(V_CURSOR, V_COL_SEQ, V_LINE);
V_FIRST := V_LINE;
DBMS_SQL.COLUMN_VALUE(V_CURSOR, V_COL_SEQ+3, V_LINE);
V_SECOND := V_LINE;
IF V_FIRST <> V_SECOND THEN
V_FINAL := V_FINAL || rec_tab(v_col_seq).col_name || ':' || V_FIRST ||'->'||V_SECOND || ',';
END IF;
end if;
END LOOP;
END LOOP;
RETURN V_FINAL;
end;
getdiff 函数的输出是clob格式,因为varchar2数据类型的限制是32767所以在达到限制函数后给我们错误。
使用:强>
select to_char(getdiff('AAAjOuAAEAAA697AAC','AAAjOuAAEAAA697AAk')) from dual;
这里to_char函数用于将clob数据格式转换为char,这样就可以给我们提供完美的字符串输出。