我有一个包含30多个字段的表格,我想获得字段名称和不同的值。
例如在表X中我们有
ID | City | State | Zip | Segment_One | Segment_Two | ....
1 | | NY | 14228 | X71 | 5 |
2 | JamesTown | NY | 14845 | X72 | 5 |
查询应该返回字段名称和数据的差异。
ID | City | Zip | Segment_One
1 | | 14228 | X71
2 | JamesTown | 14845 | X72
我可以使用一个查询来始终将行限制为两行....所以我想我总会在两行之间进行比较,但是我如何找到字段名称的差异并获取那些值的值字段?
编辑:这是我尝试过的,但它似乎会返回状态,即使它是不同的。
select * from
(select a.* from X a where ROWNUM = 1 order by last_updt_date desc )
minus
select * from
(select b.* from X b where ROWNUM = 2 order by last_updt_date desc );
结果:
ID | City | State | Zip | Segment_One | Segment_Two |
1 | | NY | 14228 | X71 | 5 | ...
这将返回行中的所有字段,但我只想要差异字段和值。对于第2行,这不会返回。如果我反转减号,那么它将不返回任何内容。
答案 0 :(得分:1)
假设有两行输入且你可以标记第1行和第2行(例如通过row_number()
分析函数),这应该可以工作:
WITH your_table AS (SELECT 1 id, NULL city, 'NY' state, 14228 zip, 'X71' segement_one, 5 segment_two from dual UNION ALL
SELECT 2 id, 'JamesTown' city, 'NY' state, 14845 zip, 'X71' segement_one, 5 segment_two from dual)
SELECT ID,
CASE WHEN ID = 1 AND NVL(city, '{null}') = LEAD(NVL(city, '{null}')) OVER (ORDER BY ID) THEN NULL
WHEN ID = 2 AND NVL(city, '{null}') = LAG(NVL(city, '{null}')) OVER (ORDER BY ID) THEN NULL
ELSE city
END city,
CASE WHEN ID = 1 AND NVL(state, '{null}') = LEAD(NVL(state, '{null}')) OVER (ORDER BY ID) THEN NULL
WHEN ID = 2 AND NVL(state, '{null}') = LAG(NVL(state, '{null}')) OVER (ORDER BY ID) THEN NULL
ELSE state
END state,
CASE WHEN ID = 1 AND NVL(zip, -99999999999) = LEAD(NVL(zip, -99999999999)) OVER (ORDER BY ID) THEN NULL
WHEN ID = 2 AND NVL(zip, -99999999999) = LAG(NVL(zip, -99999999999)) OVER (ORDER BY ID) THEN NULL
ELSE zip
END zip,
CASE WHEN ID = 1 AND NVL(segement_one, '{null}') = LEAD(NVL(segement_one, '{null}')) OVER (ORDER BY ID) THEN NULL
WHEN ID = 2 AND NVL(segement_one, '{null}') = LAG(NVL(segement_one, '{null}')) OVER (ORDER BY ID) THEN NULL
ELSE segement_one
END segement_one,
CASE WHEN ID = 1 AND NVL(segment_two, -99999999999) = LEAD(NVL(segment_two, -99999999999)) OVER (ORDER BY ID) THEN NULL
WHEN ID = 2 AND NVL(segment_two, -99999999999) = LAG(NVL(segment_two, -99999999999)) OVER (ORDER BY ID) THEN NULL
ELSE segment_two
END segment_two
FROM your_table;
ID CITY STATE ZIP SEGEMENT_ONE SEGMENT_TWO
---------- --------- ----- ---------- ------------ -----------
1 14228
2 JamesTown 14845
请注意,这只会返回不匹配的列中的值,并且匹配的列中没有任何内容;您将无法仅选择不匹配的列。
但是,你可以使用case语句来输出你可以运行的select语句,它只有受影响的列,或者你可以输出一个包含不同列的列表的字符串,如果这是你所追求的;从您的帖子中可以清楚地了解您将要执行的查询结果。
答案 1 :(得分:1)
SQL不能像那样工作,因为结果集中总是必须有已知数量的列。你能做的是:
with t as
(select '1' id, '' city, 'NY' state, '14228' Zip, 'X71' Segment_One, 5 Segment_Two from dual
UNION ALL
select '2' id, 'JamesTown' city, 'NY' state, '14845' Zip, 'X72' Segment_One, 5 Segment_Two from dual
)
select *
from
(select decode(min(id), max(id), '', 'ID') as name, min(id) as min_value, max(id) as max_value from t
UNION ALL
select decode(min(city), max(city), '', 'CITY') as name, min(city) as min_value, max(city) as max_value from t
UNION ALL
select decode(min(state), max(state), '', 'STATE') as name, min(state) as min_value, max(state) as max_value from t
UNION ALL
select decode(min(zip), max(zip), '', 'ZIP') as name, min(zip) as min_value, max(zip) as max_value from t
)
where name is not null
;
NAME MIN_VALUE MAX_VALUE
----- --------- ---------
ID 1 2
ZIP 14228 14845
答案 2 :(得分:1)
您可以使用PL / SQL块和动态CURSOR
尝试这样的事情。请注意,我在这里
使用ID = 1和2作为变量,您应该知道并从某处传递。
DBMS_SQL.RETURN_RESULT
(12c及以上)用于显示动态构造的游标的输出。或者,您可以使用PRINT
命令从REFCURSOR
获取并显示o / p。
SET serveroutput ON
DECLARE
v_ref SYS_REFCURSOR;
id1 X.ID%TYPE := 1;
id2 X.ID%TYPE := 2;
v_col VARCHAR2(32);
v_cols VARCHAR2(1000);
BEGIN
FOR r IN
( SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = 'X'
)
LOOP
EXECUTE IMMEDIATE '
SELECT
CASE
WHEN a.'||r.column_name|| ' = ' || 'b.'|| r.column_name ||'
THEN a.'||r.column_name || ' END
FROM
X a
CROSS JOIN X b WHERE
a.ID = :Id1 AND b.ID = :Id2' INTO v_col USING id1,id2 ;
v_cols := v_cols ||
CASE
WHEN v_col IS NULL THEN
','||r.column_name
END;
END LOOP;
v_cols := TRIM(BOTH ',' FROM v_cols);
OPEN v_ref FOR 'select ' || v_cols || ' FROM X WHERE ID = '||id1||'
UNION ALL select ' || v_cols || ' FROM X WHERE ID = '||id2 ;
DBMS_SQL.RETURN_RESULT(v_ref);
END;
/
ResultSet #1
ID CITY ZIP SEGMENT_ONE
------------------ --------- ------------ -----------
1 14228 X71
2 JamesTown 14845 X72
PL/SQL procedure successfully completed.
答案 3 :(得分:1)
当我之前做过类似的事情时,我使用字典表为它生成sql。不确定这是否适合你。像这样的东西
select 'select '''||column_name||''' col_name, x1.'||column_name||', x2.'||column_name||' from X x1 join X x2 on x1.id=1 and x2.id=2 where x1.'||column_name||' != x2.'||column_name||';'
from dba_tab_columns where table_name='X';
如果你运行它,它将生成30个sql语句,每列一个,然后你可以运行所有这些,看看你得到了什么。如果列可以为空,则可能需要添加NVL
答案 4 :(得分:0)
您是否尝试过使用MINUS运算符 这将返回2个记录集/元组之间不同的所有记录 您必须使用案例陈述来确定哪些列不同。
答案 5 :(得分:0)
Kaushik Nayak的回答挽救了我的生活!
https://stackoverflow.com/a/49538691/14163316
我的帐户是新帐户,所以我无法写α评论,而是α完整答案。
在我的情况下,原始sql联合有一些额外的行,因此我不得不将其反转。
这是从原始答案中修改后的sql,它很漂亮。
我在代码块中留下了一些注释,以显示您必须更改的地方。
define tableName = 'TABLE'; -- Change to your table name
define idName = 'ID'; -- Change to your ID name if its not ID
SET serveroutput ON
DECLARE
id1 number:= 1; -- Change to the ID of the first row you want to compare
id2 number:= 2; -- Change to the ID of the second row you want to compare
v_ref SYS_REFCURSOR;
v_col VARCHAR2(32);
v_cols VARCHAR2(3000);
BEGIN
FOR r IN
( SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = '&tableName'
)
LOOP
EXECUTE IMMEDIATE '
SELECT
CASE
WHEN a.'||r.column_name|| ' != ' || 'b.'|| r.column_name ||'
THEN a.'||r.column_name || ' END
FROM
&tableName a
CROSS JOIN &tableName b WHERE
a.&idName = :id1 AND b.&idName = :id2' INTO v_col USING id1,id2 ;
v_cols := v_cols ||
CASE
WHEN v_col IS NOT NULL THEN
','||r.column_name
END;
END LOOP;
v_cols := TRIM(BOTH ',' FROM v_cols);
OPEN v_ref FOR 'select ' || v_cols || ' FROM &tableName WHERE &idName = '||id1||'
UNION ALL select ' || v_cols || ' FROM &tableName WHERE &idName = '||id2 ;
DBMS_SQL.RETURN_RESULT(v_ref);
END;
/
再次感谢Kaushik Nayak的原始答案!