How to compare two tables column by column in oracle - sql

I have two similar tables in oracle in two different databases.
For example : my table name is EMPLOYEE and primary key is employee id. The same table with same columns(say 50 columns are is avlbl in two databases and two databases are linked.
I want to compare these two tables column by column and find out which records are not matching. i want the specific column in each row in two tables that are not matching.

select *
from
(
( select * from TableInSchema1
minus
select * from TableInSchema2)
union all
( select * from TableInSchema2
minus
select * from TableInSchema1)
)
should do the trick if you want to solve this with a query

As an alternative which saves from full scanning each table twice and also gives you an easy way to tell which table had more rows with a combination of values than the other:
SELECT col1
, col2
-- (include all columns that you want to compare)
, COUNT(src1) CNT1
, COUNT(src2) CNT2
FROM (SELECT a.col1
, a.col2
-- (include all columns that you want to compare)
, 1 src1
, TO_NUMBER(NULL) src2
FROM tab_a a
UNION ALL
SELECT b.col1
, b.col2
-- (include all columns that you want to compare)
, TO_NUMBER(NULL) src1
, 2 src2
FROM tab_b b
)
GROUP BY col1
, col2
HAVING COUNT(src1) <> COUNT(src2) -- only show the combinations that don't match
Credit goes here: http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:1417403971710

It won't be fast, and there will be a lot for you to type (unless you generate the SQL from user_tab_columns), but here is what I use when I need to compare two tables row-by-row and column-by-column.
The query will return all rows that
Exists in table1 but not in table2
Exists in table2 but not in table1
Exists in both tables, but have at least one column with a different value
(common identical rows will be excluded).
"PK" is the column(s) that make up your primary key.
"a" will contain A if the present row exists in table1.
"b" will contain B if the present row exists in table2.
select pk
,decode(a.rowid, null, null, 'A') as a
,decode(b.rowid, null, null, 'B') as b
,a.col1, b.col1
,a.col2, b.col2
,a.col3, b.col3
,...
from table1 a
full outer
join table2 b using(pk)
where decode(a.col1, b.col1, 1, 0) = 0
or decode(a.col2, b.col2, 1, 0) = 0
or decode(a.col3, b.col3, 1, 0) = 0
or ...;
Edit
Added example code to show the difference described in comment.
Whenever one of the values contains NULL, the result will be different.
with a as(
select 0 as col1 from dual union all
select 1 as col1 from dual union all
select null as col1 from dual
)
,b as(
select 1 as col1 from dual union all
select 2 as col1 from dual union all
select null as col1 from dual
)
select a.col1
,b.col1
,decode(a.col1, b.col1, 'Same', 'Different') as approach_1
,case when a.col1 <> b.col1 then 'Different' else 'Same' end as approach_2
from a,b
order
by a.col1
,b.col1;
col1 col1_1 approach_1 approach_2
==== ====== ========== ==========
0 1 Different Different
0 2 Different Different
0 null Different Same <---
1 1 Same Same
1 2 Different Different
1 null Different Same <---
null 1 Different Same <---
null 2 Different Same <---
null null Same Same

Try to use 3rd party tool, such as SQL Data Examiner which compares Oracle databases and shows you differences.

Using the minus operator was working but also it was taking more time to execute which was not acceptable.
I have a similar kind of requirement for data migration and I used the NOT IN operator for that.
The modified query is :
select *
from A
where (emp_id,emp_name) not in
(select emp_id,emp_name from B)
union all
select * from B
where (emp_id,emp_name) not in
(select emp_id,emp_name from A);
This query executed fast. Also you can add any number of columns in the select query.
Only catch is that both tables should have the exact same table structure for this to be executed.

SELECT *
FROM (SELECT table_name, COUNT (*) cnt
FROM all_tab_columns
WHERE owner IN ('OWNER_A')
GROUP BY table_name) x,
(SELECT table_name, COUNT (*) cnt
FROM all_tab_columns
WHERE owner IN ('OWNER_B')
GROUP BY table_name) y
WHERE x.table_name = y.table_name AND x.cnt <> y.cnt;

Used full outer join -- But it will not show - if its not matched -
SQL> desc aaa - its a table
Name Null? Type
A1 NUMBER
B1 VARCHAR2(10)
SQL> desc aaav -its a view
Name Null? Type
A1 NUMBER
B1 VARCHAR2(10)
SQL> select a.column_name,b.column_name from dba_tab_columns a full outer join dba_tab_columns b on a.column_name=b.column_name where a.TABLE_NAME='AAA' and B.table_name='AAAV';
COLUMN_NAME COLUMN_NAME
A1 A1
B1 B1

Related

How, in Oracle, to SELECT FROM a table or another depending on certain criteria?

I have a query that must choose from one table if certain criteria is meet or from another if the criteria is different.
In my case i need to select from Table_A if we are on Database_A or from Table_B if we are on Database_B, but the criteria could be a different one.
I want to do something like this:
SELECT
COLUMN_1, COLUMN_2, ..., COLUMN_N
FROM
(if database is Database_A then Table_A
but if database is Database_B then from Table B
else... it could be DUAL if any other situation, it will throw an error, but i can manage it)
WHERE
(my where criteria)
How can i do it with pure SQL not PL-SQL? I can use nested queries or WITH or similar stuff, but not "coding". I cannot create tables or views, i can only query the database for data.
I tried using CASE or other options, with no luck.
You may use the database name in the condition criteria and UNION operator to select from the right table.
SELECT COLUMN_1, COLUMN_2, ..., COLUMN_N
FROM
(
SELECT COLUMN_1, COLUMN_2, ..., COLUMN_N
FROM TableA
WHERE ora_database_name = 'DatabaseA'
UNION ALL
SELECT COLUMN_1, COLUMN_2, ..., COLUMN_N
FROM TableB
WHERE ora_database_name = 'DatabaseB'
UNION ALL
SELECT 'ERROR', null, ..., null
FROM DUAL
WHERE ora_database_name NOT IN ('DatabaseA', 'DatabaseB')
)
WHERE
(my where criteria)
If it is the case of selecting from one of two tables then something like this could help:
WITH
tab_a AS
( Select COL_1, COL_2, COL_3 From TABLE_A ), -- It is possible to do the filter here or in the main SQL or both
tab_b AS
( Select COL_1, COL_2, COL_3 From TABLE_B ) -- It is possible to do the filter here or in the main SQL or both
Select DISTINCT
CASE WHEN 1 = 1 THEN a.COL_1 ELSE b.COL_1 END "COL_1",
CASE WHEN 1 = 1 THEN a.COL_2 ELSE b.COL_2 END "COL_2",
CASE WHEN 1 = 1 THEN a.COL_3 ELSE b.COL_3 END "COL_3"
From
tab_a a
Inner Join
tab_b b ON(1 = 1)
Where
CASE WHEN 1 = 1 THEN b.COL_1 ELSE a.COL_2 END = 'XXX' -- filter could be set on the same columns or on different ones of the same type
You can put the where clause either in the cte definition (WITH clause) or in the main SQL or in both.
WHEN condition within CASE expresions should be put acording to your data context and that is the place that will select/filter the data from one or another table.
INNER JOIN ON condition may stay as is (1=1) for it creates cross link that will be solved by the DISTINCT keyword.
If both tables are not reachable from both databases then you shall have a separate scripts, one for each db. You can, though, check which is the active db and/or if there is a specific table.
-- to get the db name ...
Select NAME "DB_NAME" From V$database
-- ... to check if the table exist ...
Select TABLE_NAME from ALL_TABLES where TABLE_NAME = 'TABLE_A'
-- ... or some additional info besides the table name
Select
u.USER_ID, u.USERNAME,
t.TABLE_NAME, t.OWNER "TABLE_OWNER"
From ALL_USERS u
Inner Join ALL_TABLES t ON(t.OWNER = u.USERNAME)
Where TABLE_NAME = 'TABLE_A'

How to write redshift aws query to search for a value in comma delimited values

table1
user_id
country_code
1
'IN,AU,AC'
2
'MX,IN'
table2
user_id
valid_country
1
'IN'
1
'AU'
2
'MX'
3
'YT'
4
'RU'
As you can see, some entries in the country_code column are multiple codes separated by commas. I would like to print user_id in table1 and their corresponding country_code only if they are valid. To check for validity here I need to use table2 which has user_id and valid_country.
The desired output is:
user_id
country_code
1
'IN'
1
'AU'
2
'MX'
Query looks like
select tb1.user_id, country_code from table1 tb1, table2 tb2 where
tb1.user_id=tb2.user_id and <Here I need to check if tb2.country_code
is there in tb1.country_code (codes separated by commas)>
Are there any simple solution that I could check valid_country in the comma separated values.
The simple way isn't always the best. There are a number of corner cases that can arise here (like are all country codes 2 letters). That said a LIKE clause would be simple:
select tb1.user_id, valid_country as country_code
from table1 tb1, table2 tb2
where tb1.user_id=tb2.user_id
and tb1.country_code like '%'||tb2.valid_country||'%'
Or if we are to put this in modern SQL syntax:
select tb1.user_id, valid_country as country_code
from table1 tb1 join table2 tb2
on tb1.user_id=tb2.user_id
and tb1.country_code like '%'||tb2.valid_country||'%'
Try this:
a) Verticalise tb1 by CROSS JOINing it with a series of consecutive integers (which I supply in a Common Table Expression), and applying the SPLIT_PART() function to break the comma delimited list into single element.
b) INNER JOIN the verticalised result with the valid user_id/country code combinations table on an equi-join on both columns.
WITH
-- your table 1, don't use in end query ...
tb1(user_id,country_code) AS (
SELECT 1,'IN,AU,AC'
UNION ALL SELECT 2,'MX,IN'
)
,
-- your table 2, don't use in end query ...
tb2(user_id,valid_country) AS (
SELECT 1,'IN'
UNION ALL SELECT 1,'AU'
UNION ALL SELECT 2,'MX'
UNION ALL SELECT 3,'YT'
UNION ALL SELECT 4,'RU'
)
-- real query starts here, replace following comma with "WITH" ...
,
i(i) AS ( -- need a series of integers ...
SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
)
,
vertical AS (
SELECT
tb1.user_id
, i
, SPLIT_PART(country_code,',',i) AS valid_country
FROM tb1 CROSS JOIN i
WHERE SPLIT_PART(country_code,',',i) <> ''
)
SELECT
vertical.user_id
, vertical.valid_country
FROM vertical
JOIN tb2 USING(user_id,valid_country)
ORDER BY vertical.user_id,vertical.i
;
-- out user_id | valid_country
-- out ---------+---------------
-- out 1 | IN
-- out 1 | AU
-- out 2 | MX

To check if string contains specific number or not

I have two columns in two different tables.
First column is number like 0493484402 and second column is audit_detail like 'addr_mastersubscription has changed from 32488141893 to 32488141973'.
Audit detail column may have different type of string other than above. I have to check only in above type mentioned strings
I have to check whether first column value is present or not in second column at position of Second number.
If the number is not present I need that number as output
I am using oracel SQL developer
Second column datatype is clob and there is not comman filed in both table's
First column data type is varchar
Use REGEXP_LIKE:
SELECT *
FROM yourTable
WHERE REGEXP_LIKE (col2, ' to ' || col1 || '$');
You need to check the two conditions then you can use REGEXP_SUBSTR and REGEXP_LIKE as follows:
SELECT * FROM YOUR_TABLE
WHERE REGEXP_LIKE(SECOND_COLUMN, '^addr_mastersubscription has changed from [0-9]+ to [0-9]+$')
AND TO_NUMBER(FIRST_COLUMN) = TO_NUMBER(REGEXP_SUBSTR(SECOND_COLUMN,'[0-9]+$'))
This is how I understood the question.
Sample data (lines #1 - 9) contain additional ID column (unless you're planning to do cross join between those two tables) which is used to join taba and tabb.
regexp_substr looks for the second number in tabb's col column, which is compared to taba.col. I displayed whether it exists there or not; you can display whatever you want.
Query you might need begins at line #11.
SQL> with
2 taba (id, col) as
3 (select 1, '0493484402' from dual union all
4 select 2, '012345' from dual
5 ),
6 tabb (id, col) as
7 (select 1, 'addr_mastersubscription has changed from 32488141893 to 32488141973' from dual union all
8 select 2, 'nothing changed from 098776 to 012345' from dual
9 )
10 --
11 select a.id,
12 case when a.col = regexp_substr(b.col, '\d+', 1, 2) then a.col || ' exists in tabb'
13 else a.col || ' does not exist in tabb'
14 end result
15 from taba a join tabb b on a.id = b.id;
ID RESULT
---------- ---------------------------------
1 0493484402 does not exist in tabb
2 012345 exists in tabb
SQL>
You can use INSTR(), names itself defines in string where we can check if a particular string is available in the respective column.
Please use below query,
select t1.* from table1 t1
inner join table2 t2
on (instr(t1.first_column, t2.second_column) = 0);
instr(t1.first_column, t2.second_column) = 0 This condition provides you unmatching columns
instr(t1.first_column, t2.second_column) > 0 This condition provides you matching strings

Identify the difference in column in a specific row for a particular key column

I have two tables in SQL Server, table1 and table2. Both have the same schema and equal number of rows. I am trying to find if there is any difference in any column value for particular rows.
I did with Except to find the difference, but there is millions of rows, so trying to customize.
Code:
select * from T1
except
select * from T2
But the above code does not return the correct result.
CREATE TABLE T1
(
KEYCOL VARCHAR(60),
COL2 CHAR(20),
COL3 INT,
COL4 VARCHAR(30)
)
INSERT INTO T1
SELECT 1000004177 R09 1 909622 UNION ALL
SELECT 1000004478 Q22 1 3659573 UNION ALL
SELECT 1000008983 Q16 1 955987 UNION ALL
SELECT 1000010178 XX1 1 3069968 UNION ALL
SELECT 1000013347 R09 1 3679779 UNION ALL
SELECT 1000014510 Q16 1 YYY23 UNION ALL
SELECT 1000015230 R17 1 1000015230 UNION ALL
SELECT 1000016049 Q16 1 1000016049 UNION ALL
SELECT 1000016332 Q16 1 1000016332
CREATE TABLE T2
(
KEYCOL VARCHAR(60),
COL2 CHAR(20),
COL3 INT,
COL4 VARCHAR(30)
)
INSERT INTO T2
SELECT 1000004177 R09 1 909622 UNION ALL
SELECT 1000004478 Q22 1 3659573 UNION ALL
SELECT 1000008983 Q16 1 955987 UNION ALL
SELECT 1000010178 ZZZ 1 3069968 UNION ALL
SELECT 1000013347 R09 1 3679779 UNION ALL
SELECT 1000014510 Q16 1 ZZZ23 UNION ALL
SELECT 1000015230 R17 1 1000015230 UNION ALL
SELECT 1000016049 Q16 1 1000016049 UNION ALL
SELECT 1000016332 Q16 1 1000016332
Desired output:
1000004177 NO CHANGE
1000004478 NO CHANGE
1000008983 NO CHANGE
1000010178 CHANGE IN COL2
1000013347 NO CHANGE
1000014510 CHANGE IN COL4
1000015230 NO CHANGE
1000016049 NO CHANGE
1000016332 NO CHANGE
May you please share yours thoughts.
You can use join and some conditional logic. Something like this:
select t1.keycol,
stuff( (case when t1.col2 <> t2.col2 then ', CHANGE IN COL2' else '' end) +
(case when t1.col3 <> t2.col3 then ', CHANGE IN COL3' else '' end) +
(case when t1.col4 <> t2.col4 then ', CHANGE IN COL4' else '' end)
1, 2, '')
from t1 join
t2
on t1.keycol = t2.keycol;
This version produces '' when the columns are all the same rather than 'NO CHANGE'.
You can create a table with Primary Key and hash of all columns in the table.
Few samples are here:
SQL way to get the MD5 or SHA1 of an entire row
Insert keys and hashes from both source tables into the hash table.
After creating such table you can run:
SELECT PrimaryKey,Hash
FROM HashTable
GROUP BY PrimaryKey,Hash
HAVING COUNT(1) = 1
This query will identify different rows.
There is always a possibility of hash collisions, but probability is so low that you may ignore it.

SQL - Return a default value when my search returns no results along with search criteria

I am searching with a query
--Code Format
SELECT COLA,COLB,COLC from MYTABLE where SWITCH IN (1,2,3);
If MYTABLE does not contain rows with SWITCH 1,2 or 3 I need default values returned along with the SWITCH value. How do I do it?
Below is my table format
COLA | COLB | COLC | SWITCH
------------------------------
A B C 1
a b c 2
i want a query when I search with
select * from MYTABLE where switch in (1,2,3)
That gets results like this --
COLA | COLB | COLC | SWITCH
------------------------------
A B C 1
a b c 2
NA NA NA 3
--Check to see if any row exists matching your conditions
IF NOT EXISTS (SELECT COLA,COLB,COLC from MYTABLE where SWITCH IN (1,2,3))
BEGIN
--Select your default values
END
ELSE
BEGIN
--Found rows, return them
SELECT COLA,COLB,COLC from MYTABLE where SWITCH IN (1,2,3)
END
if not exists( SELECT 1 from MYTABLE where SWITCH IN (1,2,3))
select default_value
How about:
SELECT COLA,COLB,COLC from MYTABLE where SWITCH IN (1,2,3)
union select 5555, 6666, 7777 where not exists (
SELECT COLA,COLB,COLC from MYTABLE where SWITCH IN (1,2,3)
);
5555, 6666, 7777 being the default row in case there aren't any rows matching your criteria.
Here is one way to tackle this. You need a table of the SWITCH values you want to look at. Then a simple left join makes this super easy.
select ColA
, ColB
, ColC
v.Switch
from
(
values
(1)
, (2)
, (3)
)v (Switch)
left join YourTable yt on yt.Switch = v.Switch
You can Use a Split Function And Left Join As Shown Below:
Select ISNULL(ColA,'NA') As ColA,ISNULL(ColB,'NA') As ColB,ISNULL(ColC,'NA') As ColC,ISNULL(Switch,a.splitdata)
from [dbo].[fnSplitString]('1,2,3',',') a
LEFT JOIN #MYTABLE t on a.splitdata=t.Switch
[dbo].[fnSplitString] is a Split Function with 2 arguments - Delimeter Separated String and Delimeter and Output a Table.
EDIT:
Given the new explanation, I changed the answer completely. I think I got your question now:
SELECT * FROM MYTABLE AS mt
RIGHT JOIN (SELECT 1 AS s UNION SELECT 2 AS s UNION SELECT 3 AS s) AS st
ON st.s = mt.SWITCH
You could change the SELECT 1 AS s UNION SELECT 2 AS s UNION SELECT 3 AS spart to a subquery that results in all possible values SWITCH could assume. E.g.:
SELECT DISTINCT SWITCH FROM another_table_with_all_switches
If all want is the value of switch that is not in MYTABLE, not the whole table with null values, you could try:
SELECT * FROM
(SELECT 1 AS s UNION SELECT 2 AS s UNION SELECT 3) AS st
WHERE st.s NOT IN (SELECT DISTINCT SWITCH FROM MYTABLE)