Мне нужно сравнить данные в двух таблицах сотрудников Oracle по столбцам и создать такой отчет (сравните Ato B)
Таблица А
+-------+------+---------+
|emp_num| name | a_city |
+-------+------+---------+
| 1 | Jo | Lay |
| 2 | Paul | Dubai |
+-------+------+---------+
Таблица Б
+-------+------+---------+
|emp_num| name | a_city |
+-------+------+---------+
| 1 | Jo | Sydney |
+-------+------+---------+
После сравнения данных в выводах A и B, как показано ниже.
+----------+----------+-----------+---------------+----------+---------+------------+-------+-------+-------------+
| status | a_emp_num| b_emp_num | emp_num_match | a_city | b_city | city_match | a_name| b_name| name_match |
+----------+----------+-----------+---------------+----------+---------+------------+-------+-------+-------------+
| Found | 1 | 1 | Matched | Lay | Lay | Matched | Jo | Jo | Matched |
| Not Found| 2 | | Not Matched | Dubai | | Not Matched| Paul | | Not Matched|
+----------+----------+-----------+---------------+----------+---------+------------+-------+-------+-------------+
У меня есть запрос, созданный для сравнения этого
SELECT (CASE
WHEN A.EMP_NUM IS NULL THEN
'Found'
ELSE
'Not Found'
END) AS STATUS,
A.EMP_NUM A_EMP_NUM,
B.EMP_NUM B_EMP_NUM,
(CASE
WHEN NVL(TRIM(B.EMP_NUM), 0) = NVL(TRIM(B.EMP_NUM), 0) THEN
'Found'
ELSE
'Not Found'
END) AS EMP_NUM_MATCH
FROM A, B
WHERE A.EMP_NUM = B.EMP_NUM(+)
теперь проблема в том, что мне нужно написать оператор case для многих атрибутов, таких как ИМЯ, ВОЗРАСТ, ГОРОД (в реальном случае 400+ атрибутов)
Есть ли способ автоматически создать этот оператор выбора, если мы передадим список столбцов в качестве входных данных? или любой способ сделать это сравнение для всех столбцов в excel без явного написания выбора для каждого из столбцов
Вы можете использовать LEFT JOIN
следующим образом:
select CASE WHEN B.EMP_NUM is NOT null THEN 'Found' ELSE 'Not Found' END AS STATUS,
A.EMP_NUM A_EMP_NUM,
B.EMP_NUM B_EMP_NUM,
CASE WHEN B.EMP_NUM is NOT null THEN 'Match' ELSE 'Mismatch' END AS EMP_NUM_MATCH,
A.CITY AS A_CITY,
B.CITY AS B_CITY,
CASE WHEN A.CITY = B.CITY THEN 'Match' ELSE 'Mismatch' END AS CITY_MATCH,
A.NAME AS A_NAME,
B.NAME AS B_NAME,
CASE WHEN A.NAME = B.NAME THEN 'Match' ELSE 'Mismatch' END AS NAME_MATCH
from A LEFT JOIN B ON A.EMP_NUM =B.EMP_NUM
Вы можете использовать USER_TAB_COLS и создать выражения запроса SELECT - CASE WHEN B.EMP_NUM is NOT null THEN 'Found' ELSE 'Not Found' END AS STATUS, A.EMP_NUM A_EMP_NUM, B.EMP_NUM B_EMP_NUM
для каждого столбца.
как включить USER_TAB_COLS в sql, извините, я не уверен в этом
Вы не можете добавить USER_TAB_COLS
в этот запрос, но вы можете использовать следующий запрос для создания исходного запроса: SELECT 'CASE WHEN B.' || COLUMN_NAME ||' is NOT null THEN ''Match'' ELSE ''Mismatch'' END AS ' || COLUMN_NAME ||'_MATCH, A.' || COLUMN_NAME ||' AS ' || COLUMN_NAME ||', B.' || COLUMN_NAME ||' AS ' || COLUMN_NAME ||',' FROM USER_TAB_COLS WHERE TABLE_NAME = 'YOUR_A_TABLE_NAME' ORDER BY COLUMN_ID;
Вы должны скопировать и вставить каждую строку и использовать ее в своем запросе.
Напишите запрос, который напишет запрос для вас. Что-то вроде этого:
DECLARE
l_str VARCHAR2 (4000) := 'select ';
BEGIN
FOR cr IN ( SELECT a.column_name cn
FROM user_tab_columns a
WHERE a.table_name = 'TABLE_A'
ORDER BY column_id)
LOOP
l_str :=
l_str
|| 'a.'
|| cr.cn
|| ', b.'
|| cr.cn
|| ', '
|| 'case when nvl(trim(a.'
|| cr.cn
|| '), 0) = nvl ( trim ( b . '
|| cr.cn
|| '), 0) then ''match'' else ''mismatch'' end '
|| cr.cn
|| ',';
END LOOP;
l_str :=
RTRIM (l_str, ',')
|| ' from table_a a left join table_b b on a . empno = b . empno ';
DBMS_OUTPUT.put_line (l_str);
END;
/
Результатом является утверждение SELECT
; Я отформатировал его так, чтобы его было легче читать:
SELECT a.EMPNO,
b.EMPNO,
CASE
WHEN NVL (TRIM (a.EMPNO), 0) = NVL (TRIM (b.EMPNO), 0) THEN 'match'
ELSE 'mismatch'
END EMPNO,
a.NAME,
b.NAME,
CASE
WHEN NVL (TRIM (a.NAME), 0) = NVL (TRIM (b.NAME), 0) THEN 'match'
ELSE 'mismatch'
END NAME,
a.CITY,
b.CITY,
CASE
WHEN NVL (TRIM (a.CITY), 0) = NVL (TRIM (b.CITY), 0) THEN 'match'
ELSE 'mismatch'
END CITY
FROM table_a a LEFT JOIN table_b b ON a.empno = b.empno
который - при выполнении - возвращает
EMPNO EMPNO EMPNO NAME NA NAME CITY CITY CITY
---------- ---------- -------- ---- -- -------- ----- ------ --------
1 1 match Jo Jo match Lay Sydney mismatch
2 mismatch Paul mismatch Dubai mismatch
Теперь, когда вы знаете «как», сделайте его красивее.
Функции полного соединения и декодирования отлично работают вместе для всех случаев несоответствия в обеих таблицах, поскольку декодирование декодирования хорошо справляется с нулевыми значениями.
select
decode(a.empno, b.empno, 'Found', 'NotFound') status
, a.empno "A.EMPNO", b.empno "B.EMPNO"
, decode(a.empno, b.empno, 'Match', 'Mismatch') "EMP_NUM.MATCH"
, a.city "a.CITY", b.city "B.CITY"
, decode(a.city, b.city, 'Match', 'Mismatch') "CITY.MATCH"
, a.name "A.NAME", b.name "B.NAME"
, decode(a.name, b.name, 'Match', 'Mismatch')"NAME.MATCH"
from tableA a
full join tableB b on (a.empno = b.empno)
;
Одним из вариантов динамического поворота ваших данных будет создание функции с использованием функций XMLELEMENT()
, XMLAGG()
и NVL2()
вместе для создания оператора SELECT
, такого как
CREATE OR REPLACE FUNCTION get_city_people_info RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_str VARCHAR2(32767);
BEGIN
SELECT RTRIM(DBMS_XMLGEN.CONVERT(
XMLAGG(
XMLELEMENT(e,'A.'||column_name||' AS "a_'||column_name||'",
B.'||column_name||' AS "b_'||column_name||'",
DECODE( NVL2(A.'||column_name||',TRIM(A.'||column_name||'),'' ''),
NVL2(B.'||column_name||',TRIM(B.'||column_name||'),'' ''),
''Matched'',''Not Matched'' ) AS "'||column_name||'_match",')
).EXTRACT('//text()').GETCLOBVAL() ,1),',')
INTO v_str
FROM
(
SELECT DISTINCT LOWER(c.column_name) AS column_name
FROM user_tab_cols c
WHERE c.table_name = 'A' -- considering both tables(A and B) have exactly common columns
);
v_sql :=
'SELECT NVL2(B.emp_num , ''Found'', ''Not Found'') AS "status",'|| v_str ||'
FROM A
LEFT JOIN B
ON B.emp_num = A.emp_num';
OPEN v_recordset FOR v_sql;
RETURN v_recordset;
END;
/
который даст тип возвращаемого значения SYS_REFCURSOR
, затем вызовите
VAR rc REFCURSOR
EXEC :rc := get_city_people_info;
PRINT rc
из командной строки SQL Developer.
Demo with generated SELECT Statement Within The Function
P.S: Предпочитайте использовать предложения JOIN для ваших запросов, а не старый стиль с запросами табличного типа, разделенными запятыми.
Привет. Мой оператор выбора работает нормально... но проблема в том, что у меня есть более 300 столбцов, и столбцы будут меняться, и мне очень сложно написать оператор case для каждого столбца, есть ли способ создать запрос выбора для всех столбцов в таблице или сделать оператор case для всех столбцов в таблице