PostgreSQL query for equality between two tables - sql

I have two tables A & B and i would like to have a query that :
return TRUE ONLY if the two tables are the same (i mean every lines in A are present in B & vice versa, no matter the line order)
I have use the keyword EXCEPT but it doesnt work in many cases
Thanks for your help.

select * from tablea except all select * from tableb
returns all rows from tablea that do not exist in tableb.
doing it the other way around
select * from tableb except all select * from tablea
returns all rows in tableb that do not exist in tablea.
so, now we can:
select count(*) from ( select * from tablea except all select * from tableb ) x;
to get number of "bad" rows in tablea, and:
select count(*) from ( select * from tableb except all select * from tablea ) x;
to get count of "bad" rows in tableb.
tables are the same if both counts are 0. and since neither count can be less than zero, then we can test if sum of the counts is 0:
select 0 = ( select count(*) from ( select * from tablea except all select * from tableb ) x ) + ( select count(*) from ( select * from tableb except all select * from tablea ) x );

Related

SQL Select where condition : value 1 <> value 2

Need your help to know if possible to select values from a table with the below condition :
Table content : matching between 2 objects
(Id_obj_A; name_obj_A; country_obj_A; Id_obj_B; name_obj_B; country_obj_B)
Select *
from table
Where (only if country_obj_A <> country_obj_B)
Many thanks for your help
Yes. There are a few ways, one is to use NOT EXISTS like this:
select
*
from tableA
where NOT EXISTS (
select NULL
from tableB
where tableB.country_obj_B = tableA.country_obj_A
)
or, using NOT IN
select
*
from tableA
where country_obj_A NOT IN (
select country_obj_B
from tableB
)
or, using a LEFT JOIN then exclude the joined rows:
select
*
from tableA
left join tableB on tableA.country_obj_A = tableB.country_obj_B
where tableB.country_obj_B IS NULL

Apply inner join on two tables constructed with minus

I have two versions of the same table and want to find the differences between both: which rows have changed? I use a minus query twice to print the changed rows as they appear in the old and new table.
Now I want to add a new query: one that shows me the rows that have changed on a specific column.
(select * from NewTable minus select * from OldTable) NewRows
inner join
(select * from OldTable minus select * from NewTable) OldRows
on NewRows.column1 = OldRows.column1
and NewRows.column2 <> OldRows.column2
where column1 is the unique row id and column 2 is the changed property.
When I execute Oracle SQL Developer I get error ORA-00933 "SQL command not properly ended", and he indicates the definition of NewRows as error. I have also tried with ") as NewRows" but it did not work.
The following query does work, so the NewTable and OldTable are compatible.
(select * from NewTable minus select * from OldTable)
union
(select * from OldTable minus select * from NewTable)
Try with additional select * from on the beginning:
select * from
(select * from NewTable minus select * from OldTable) NewRows
inner join
(select * from OldTable minus select * from NewTable) OldRows
on (NewRows.column1 = OldRows.column1 and NewRows.column2 <> OldRows.column2)
Also be aware of nulls your condition for difference <> will not cover situation where one value is null and second is not null. You should probably use:
on (nvl(NewRows.column1, 'UNIQUEVAL1') = nvl(OldRows.column1, 'UNIQUEVAL1')
and nvl(NewRows.column2, 'UNIQUEVAL2') <> nvl(OldRows.column2, 'UNIQUEVAL2') )

Compare two tables in postgres

I have two tables with identical definition:
create tabA (
user_id int,
contact boolean,
promote boolean
);
create tabB (
user_id int,
contact boolean,
promote boolean
);
I want to compare two columns contact and promote and see if there is any discrepancy in row data. For example:
row from tabA: 1,T,T
row from tabB: 1,T,F
So there is discrepancy now I want to catch that and select only those rows where they are not equal.
SELECT * FROM tabA, tabB
WHERE tabA.user_id = tabA.user_id
AND (
tabA.contact != tabB.contact
OR
tabA.promote != tabB.promote
);
As long as there can be NULL values, you need to use null-safe operators:
SELECT user_id, a.contact AS a_contact, a.promote AS a_promote
, b.contact AS b_contact, b.promote AS b_promote
FROM tabA a
JOIN tabB b USING (user_id)
WHERE a.contact IS DISTINCT FROM b.contact OR
a.promote IS DISTINCT FROM b.promote;
Another option is to use Postgres' record comparison capability:
select *
from taba a
full join tabb b using (user_id)
where a is distinct from b;
To find differences in table content you should compare 3 column's values of next query:
select (
select count(*) from (
select * from a
union
select * from b
) m
) merged,
(select count(*) from a) in_a,
(select count(*) from b) in_b;
If value in merged column is not equal to value in columns in_a and in_b then the content of table a and b has at least one difference.

Find Modified/New/Deleted Records Between Two Tables

I want to find new, modified and deleted records in one table (tableA) by comparing it to another table (tableB). Both tables are of the same schema and has a unique ID field.
In my situation, tableA is originally the same as tableB but it has been edited by some external organisation and once they have done their edits, they send the table back via ZIP file, and we re-populate (truncate and insert) that data to tableA. So I want to find out what records have changed in tableA. I am using SQL Server 2012.
I can get new and modified records with the "except" keyword:
select * from tableA
except
select * form tableB
(Let's call the above results ResultsA)
I can also get deleted and modified records:
select * from tableB
except
select * form tableA
(Let's call the above results ResultsB)
The problem is, both ResultsA and ResultsB have the same records that have been modified/edited. So the modified/edited records are doubled up. I can use inner join or intersect on ResultsA and ResultsB to get just the modified records (call this results ResultsC). But then I will need to use join/except again between ResultsA and ResultsC to get just the new records, and join/except again between ResultsB and ResultsC to get just the deleted records... I tried this and this but they are not working for me.
Obviously this is not good. Are there any elegant and simpler ways to find out the records that have been deleted, modified or added in tableA compared to tableB?
How about:
-- DELETED
SELECT B.*, 'DELETED' AS 'CHANGE_TYPE'
FROM TableB B
LEFT JOIN TableA A ON B.PK_ID = A.PK_ID
WHERE A.PK_ID IS NULL
UNION
-- NEW
SELECT A.*, 'NEW' AS 'CHANGE_TYPE'
FROM TableA A
LEFT JOIN TableB B ON B.PK_ID = A.PK_ID
WHERE B.PK_ID IS NULL
UNION
-- MODIFIED
SELECT B.*, 'MODIFIED' AS 'CHANGE_TYPE'
FROM (
SELECT * FROM TableA
EXCEPT
SELECT * FROM TableB
) S1
INNER JOIN TableB B ON S1.PK_ID = B.PK_ID;
Not exactly elegant, but it works.
Based on what i understood i came up with the following solution.
DECLARE #tableA TABLE (ID INT, Number INT)
DECLARE #tableB TABLE (ID INT, Number INT)
INSERT INTO #tableA VALUES
(1,10),
(2,20),
(3,30),
(4,40)
INSERT INTO #tableB VALUES
(1,11),
(2,20),
(4,40),
(5,50)
SELECT *,'Modified or deleted' as 'Status' FROM
(
select * from #tableA
except
select * from #tableB
)a WHERE ID NOT IN
(
select ID from #tableB
except
select ID from #tableA
)
UNION
SELECT *,'New' as 'Status' FROM
(
select * from #tableB
except
select * from #tableA
)b WHERE ID NOT IN
(
SELECT ID FROM
(
select * from #tableA
except
select * from #tableB
)a WHERE ID NOT IN
(
select ID from #tableB
except
select ID from #tableA
)
)
You can use the OUTPUT clause:
Returns information from, or expressions based on, each row affected by an INSERT, UPDATE, or DELETE statement. These results can be returned to the processing application for use in such things as confirmation messages, archiving, and other such application requirements. Alternatively, results can be inserted into a table or table variable.
See the the following, sorry I don't have a practical code for you. But note the SQL output clause can be used to return any value from ‘inserted’ and ‘deleted’ (New value and Old value) tables when doing an insert or update. follow this for more info
declare #DBOrderItem table
(
OrderItemGuid UniqueIdentifier default newid(),
Name VarChar(100)
);
declare #PayloadOrderItem table
(
OrderItemGuid UniqueIdentifier default newid(),
Name VarChar(100)
);
insert into #DBOrderItem (Name) values ('Phone');
insert into #DBOrderItem (Name) values ('Laptop');
insert into #PayloadOrderItem
select top 1 * from #DBOrderItem;
insert into #PayloadOrderItem (Name) values ('Tablet');
select doi.OrderItemGuid,
doi.Name,
case when poi.OrderItemGuid is null then 'Delete' else 'Update' end ActionType
from #DBOrderItem doi
left join #PayloadOrderItem poi on doi.OrderItemGuid = poi.OrderItemGuid
union
select poi.OrderItemGuid,
poi.Name,
'Add' ActionType
from #PayloadOrderItem poi
left join #DBOrderItem doi on doi.OrderItemGuid = poi.OrderItemGuid
where doi.OrderItemGuid is null;
Another solution that works quite efficiently is to use a where not exists an intersect between the two tables. Its very compact.
SELECT
IsNull(tableB.ID,tableA.ID) as 'ID',
IsNull(tableB.Number,tableA.Number) as 'Number',
'Action' = CASE
WHEN tableB.ID IS NULL THEN 'Deleted'
WHEN tableA.ID IS NULL THEN 'Created'
ELSE 'Updated'
END
FROM tableA
FULL OUTER JOIN tableB
ON tableB.ID = tableA.ID
WHERE
NOT EXISTS (SELECT tableB.* INTERSECT SELECT tableA.*)
This keeps the table scans down to a minimum, and provides detection of new, deleted and changed records.
I put all three from here into fiddle, and its surprising how differently they all compile.
http://sqlfiddle.com/#!6/b1a5a/5
This one works without primary key also a bit more elegant .(in my opinion!)
WITh A AS (SELECT 1,2,3 FROM DUAL
UNION ALL
SELECT 1,3,2 FROM DUAL
UNION ALL
SELECT 1,3,1 FROM DUAL),
B AS (SELECT 1,3,2 FROM DUAL
UNION ALL
SELECT 1,2,3 FROM DUAL
UNION ALL
SELECT 1,3,5 FROM DUAL
)
,
C AS
(SELECT * FROM A
MINUS
SELECT * FROM B
),
D AS( SELECT * FROM b
MINUS
SELECT * FROM A)
SELECT C.* ,'Deleted' FROM c
UNION ALL
SELECT D.* ,'Added' FROM D

Is there a quick way to compare two equally formatted tables in SQL?

I would like to make an SQL query to compare two tables with identical columns, both names and types. Each table has a unique key. I want the query to return any rows that contain unequal values. I know could do something like this
select *
from table_1, table_2
where
table_1.key = table_2.key
and (
table_1.col1 != table_2.col1 OR
table_1.col2 != table_2.col2 OR
...
)
but this would be tedious since there are a large and potentially variable number of columns.
edit
If it helps, I'm using a tsql system.
Not sure what type of DB you are using but if you are using SQL Server 2005 or higher try this:
select 'table1' as tblName, * from
(select * from table1
except
select * from table2) x
union all
select 'table2' as tblName, * from
(select * from table2
except select *
from table1) x
How abt this..
select * from table1 where not exists (select * from table2)
union all
select * from table2 where not exists (select * from table1)
Verified with SQL Server:
(select * from table1 except select * from table2)
union
(select * from table2 except select * from table1);
Verified with Oracle:
(select * from table1 minus (select * from table2))
union
(select * from table2 minus (select * from table1))