How to compare two identicals tables data of each column in postgres? - postgresql

I want compare two table's all column values.The two table is identical tables means column number is same and primary key is same. can any one suggest query which compare such two tables in postgres.
The query should give the column name and what is the two different value of two tables.Like this
pkey | column_name | table1_value | table2_value
123 | bonus | 1 | 0

To get all different rows you can use:
select *
from table_1 t1
join table_2 t2 on t1.pkey = t2.pkey
where t1 is distinct from t2;
This will only compare rows that exist in both tables. If you also want to find those that are missing in on of them use a full outer join:
select coalesce(t1.pkey, t2.pkey) as pkey,
case
when t1.pkey is null then 'Missing in table_1'
when t2.pkey is null then 'Missing in table_2'
else 'At least one column is different'
end as status,
*
from table_1 t1
full ojoin table_2 t2 on t1.pkey = t2.pkey
where (t1 is distinct from t2)
or (t1.pkey is null)
or (t2.pkey is null);
If you install the hstore extension, you can view the differences as a key/value map:
select coalesce(t1.pkey, t2.pkey) as pkey,
case
when t1.pkey is null then 'Missing in table_1'
when t2.pkey is null then 'Missing in table_2'
else 'At least one column is different'
end as status,
hstore(t1) - hstore(t2) as values_in_table_1,
hstore(t2) - hstore(t1) as values_in_table_2
from table_1 t1
full ojoin table_2 t2 on t1.pkey = t2.pkey
where (t1 is distinct from t2)
or (t1.pkey is null)
or (t2.pkey is null);
Using this sample data:
create table table_1 (pkey integer primary key, col_1 text, col_2 int);
insert into table_1 (pkey, col_1, col_2)
values (1, 'a', 1), (2, 'b', 2), (3, 'c', 3), (5, 'e', 42);
create table table_2 (pkey integer primary key, col_1 text, col_2 int);
insert into table_2 (pkey, col_1, col_2)
values (1,'a', 1), (2, 'x', 2), (3, 'c', 33), (4, 'd', 52);
A possible result would be:
pkey | status | values_in_table_1 | values_in_table_2
-----+----------------------------------+-------------------+------------------
2 | At least one column is different | "col_1"=>"b" | "col_1"=>"x"
3 | At least one column is different | "col_2"=>"3" | "col_2"=>"33"
4 | Missing in table_1 | |
5 | Missing in table_2 | |

Example data:
create table test1(pkey serial primary key, str text, val int);
insert into test1 (str, val) values ('a', 1), ('b', 2), ('c', 3);
create table test2(pkey serial primary key, str text, val int);
insert into test2 (str, val) values ('a', 1), ('x', 2), ('c', 33);
This simple query gives a complete information on differences of two tables (including rows missing in one of them):
(select 1 t, * from test1
except
select 1 t, * from test2)
union all
(select 2 t, * from test2
except
select 2 t, * from test1)
order by pkey, t;
t | pkey | str | val
---+------+-----+-----
1 | 2 | b | 2
2 | 2 | x | 2
1 | 3 | c | 3
2 | 3 | c | 33
(4 rows)
In Postgres 9.5+ you can transpose the result to the expected format using jsonb functions:
select pkey, key as column, val[1] as value_1, val[2] as value_2
from (
select pkey, key, array_agg(value order by t) val
from (
select t, pkey, key, value
from (
(select 1 t, * from test1
except
select 1 t, * from test2)
union all
(select 2 t, * from test2
except
select 2 t, * from test1)
) s,
lateral jsonb_each_text(to_jsonb(s))
group by 1, 2, 3, 4
) s
group by 1, 2
) s
where key <> 't' and val[1] <> val[2]
order by pkey;
pkey | column | value_1 | value_2
------+--------+---------+---------
2 | str | b | x
3 | val | 3 | 33
(2 rows)

I tried all of the above answer.Thanks guys for your help.Bot after googling I found a simple query.
SELECT <common_column_list> from table1
EXCEPT
SELECT <common_column_list> from table2.
It shows all the row of table1 if any table1 column value is different from table2 column value.

Not very nice but fun and it works :o)
Just replace public.mytable1 and public.mytable2 by correct tables and
update the " where table_schema='public' and table_name='mytable1'"
select * from (
select pkey,column_name,t1.col_value table1_value,t2.col_value table2_value from (
select pkey,generate_subscripts(t,1) ordinal_position,unnest(t) col_value from (
select pkey,
(
replace(regexp_replace( -- null fields
'{'||substring(a::character varying,'^.(.*).$') ||'}' -- {} instead of ()
,'([\{,])([,\}])','\1null\2','g'),',,',',null,')
)::TEXT[] t
from public.mytable1 a
) a) t1
left join (
select pkey,generate_subscripts(t,1) ordinal_position,unnest(t) col_value from (
select pkey,
(
replace(regexp_replace( -- null fields
'{'||substring(a::character varying,'^.(.*).$') ||'}' -- {} instead of ()
,'([\{,])([,\}])','\1null\2','g'),',,',',null,')
)::TEXT[] t
from public.mytable2 a
) a) t2 using (pkey,ordinal_position)
join (select * from information_schema.columns where table_schema='public' and table_name='mytable1') c using (ordinal_position)
) final where COALESCE(table1_value,'')!=COALESCE(table2_value,'')

Related

How to select minimum UUID with left outer join?

I'm trying to select a row from a table which:
has a minimum UUID
is not referenced in another table
But I'm having problems when I try to enforce the first constraint.
Here's everything working as expected on integers:
First, create tables that look like this:
t1
+----+---------+
| id | content |
+----+---------+
| 1 | a |
| 2 | b |
| 3 | c |
+----+---------+
and
t2
+----+---------+
| id | t1_id |
+----+---------+
| 1 | 1 |
+----+---------+
postgres=# create table t1(id int, content varchar(10), primary key (id));
CREATE TABLE
postgres=# create table t2(id int, t1_id int, foreign key (t1_id) references t1(id));
CREATE TABLE
postgres=# insert into t1 values (1, 'a');
INSERT 0 1
postgres=# insert into t1 values (2, 'b');
INSERT 0 1
postgres=# insert into t1 values (3, 'c');
INSERT 0 1
postgres=# insert into t2 values (1, 1);
INSERT 0 1
Now, I want to select the row in t1 with the lowest id which doesn't appear as a foreign key in t2. I want to select the row in t1 which has id = 2 and it works as expected:
postgres=# select min(t1.id) from t1 left outer join t2 on t1.id = t2.t1_id where t2.id is null;
min
-----
2
(1 row)
However, when I try the same with UUIDs, the final query fails to return anything at all. Note, I've used the answer from this post to define a way to find minimum UUIDs:
CREATE OR REPLACE FUNCTION min(uuid, uuid)
RETURNS uuid AS $$
BEGIN
IF $2 IS NULL OR $1 > $2 THEN
RETURN $2;
END IF;
RETURN $1;
END;
$$ LANGUAGE plpgsql;
create aggregate min(uuid) (
sfunc = min,
stype = uuid,
combinefunc = min,
parallel = safe,
sortop = operator (<)
);
Now, build the tables just the same as before, use gen_random_uuid to autogenerate UUIDs:
postgres=# drop table t2;
postgres=# drop table t1;
postgres=# create table t1(id uuid default gen_random_uuid(), content varchar(10), primary key (id));
postgres=# create table t2(id int, t1_id uuid, foreign key (t1_id) references t1(id));
postgres=# insert into t1(content) ('a');
postgres=# insert into t1(content) values ('a');
postgres=# insert into t1(content) values ('b');
postgres=# insert into t1(content) values ('c');
We've successfully made three entries in t1. Add an entry to t2:
postgres=# select * from t1;
id | content
--------------------------------------+---------
b6148ae3-db56-4a4a-8d46-d5b4f04277ac | a
03abd324-8626-4fb1-9cb0-593373abf9ca | b
9f12b297-3f60-48a7-8282-e27c3aff1152 | c
(3 rows)
postgres=# insert into t2 values(1, '9f12b297-3f60-48a7-8282-e27c3aff1152');
Try to select the row from t1 with a minimum ID that doesn't appear in t2, note that this fails.
postgres=# select min(t1.id) from t1 left outer join t2 on t1.id = t2.t1_id where t2.id is null;
min
-----
(1 row)
Here we show that we can select the two unreferenced entries in t1 and we can select a minimum UUID independently:
postgres=# select t1.id from t1 left outer join t2 on t1.id = t2.t1_id where t2.id is null;
id
--------------------------------------
03abd324-8626-4fb1-9cb0-593373abf9ca
b6148ae3-db56-4a4a-8d46-d5b4f04277ac
(2 rows)
postgres=# select min(id) from t1;
min
--------------------------------------
03abd324-8626-4fb1-9cb0-593373abf9ca
(1 row)
So, something funny goes on when I try to select a minimum UUID while also trying to perform the left outer join.
EDIT: the same problem exists when using not exists:
postgres=# select min(id) from t1 where not exists (select t1_id from t2 where t2.t1_id = t1.id);
min
-----
(1 row)
but the problem doesn't appear when using not in:
postgres=# select min(id) from t1 where id not in (select t1_id from t2);
min
--------------------------------------
03abd324-8626-4fb1-9cb0-593373abf9ca
(1 row)
Found a solution, turns out the function comparing UUIDs from this post isn't correct. Here's the function I wrote, which is probably less performant, which passes all the cases it failed at before:
CREATE FUNCTION min_uuid(uuid, uuid)
RETURNS uuid AS $$
BEGIN
-- if they're both null, return null
IF $2 IS NULL AND $1 IS NULL THEN
RETURN NULL ;
END IF;
-- if just 1 is null, return the other
IF $2 IS NULL THEN
RETURN $1;
END IF ;
IF $1 IS NULL THEN
RETURN $2;
END IF;
-- neither are null, return the smaller one
IF $1 > $2 THEN
RETURN $2;
END IF;
RETURN $1;
END;
$$ LANGUAGE plpgsql;
create aggregate min(uuid) (
sfunc = min_uuid,
stype = uuid,
combinefunc = min_uuid,
parallel = safe,
sortop = operator (<)
);

Multiple select exists in a single statement

I have the following piece of code:
DROP SCHEMA IF EXISTS s CASCADE;
CREATE SCHEMA s;
CREATE TABLE s.t1 (
id1 BigInt,
id2 BigInt,
CONSTRAINT "pk1" PRIMARY KEY (id1)
)
WITH(OIDS=FALSE);
INSERT INTO s.t1 (id1, id2) VALUES (2, 22);
INSERT INTO s.t1 (id1, id2) VALUES (3, 22);
INSERT INTO s.t1 (id1, id2) VALUES (4, 24);
SELECT EXISTS (SELECT TRUE FROM s.t1 t1 WHERE t1.id1 = 2,
SELECT EXISTS (SELECT TRUE FROM s.t1 t1 WHERE t1.id2 = 22);
Is there a way to check for multiple conditions in a single statement.
I am looking for the values in two separate columns.
The answer I was looking for is something like this:
bool | bool
------+------
t | t
(1 row)
I got the solution:
SELECT EXISTS(SELECT TRUE FROM s.t1 t1 WHERE t1.id1 = 6),
EXISTS(SELECT TRUE FROM s.t1 t1 WHERE t1.id2 = 22);
Output:
exists | exists
--------+--------
f | t
(1 row)
What about
SELECT max(x.t2) as t2,
max(x.t22) as t22
FROM ((SELECT t1.id1 as t2, null as t22 FROM s.t1 t1 WHERE t1.id1 = 2)
UNION
(SELECT null as t2, t1.id2 as t22 FROM s.t1 t1 WHERE t1.id2 = 22)) as x;
Demo
EXISTS yields a Boolean value, which you can use directly:
SELECT
EXISTS (SELECT * FROM s.t1 x1
WHERE x1.id1 = 2) AS ex1
, EXISTS (SELECT * FROM s.t1 x2
WHERE x2.id2 = 22) AS ex2
;
SELECT max( case when id1=2 then 'EXISTS' ELSE 'DOESN''T EXISTS' end) id1_2,
max( case when id2=22 then 'EXISTS' ELSE 'DOESN''T EXISTS' end) id2_22,
max( case when id2=33 then 'EXISTS' ELSE 'DOESN''T EXISTS' end) id2_33
FROM s.t1
demo: http://sqlfiddle.com/#!17/85c47/11
| id1_2 | id2_22 | id2_33 |
|--------|--------|----------------|
| EXISTS | EXISTS | DOESN'T EXISTS |

PostgreSQL mass insert into a table?

I need to mass insert all the values from col_a into another table. I can do it one at a time like this:
INSERT INTO table_2 (col_a_id)
SELECT 'col_a_id'
FROM table_1
WHERE col_a = 'x';
But is there a way I can just insert all the columns?
EDIT
Lets say I have this table:
Col_a | Col_b |
------------------------
1 | a |
2 | b |
3 | c |
Instead of checking what is in col_a can I just insert each instance of col_a into a table? so I'll have 1, 2 & 3 in table_2?
INSERT INTO table_2 (col1, col2, col3, .... , coln)
SELECT col1, col2, col3, .... , coln
FROM table_1
WHERE col_a = 'x';
Note: String are separated by single quote
SELECT 'this is a string'
Fieldname use double quote:
SELECT "myFieldName", "col1"
EDIT:
If you want check all columns for 'x'
WHERE 'x' IN (col1, col2, col3, .... , coln)

T-SQL One column in multiple columns select query

I have a simple problem that I have not been able to find a solution to and I'm hoping someone on StackOverflow can help.
I currently have an example query as shown below
SELECT ID
, ColumnName
FROM Table
If I run this query I get the following result:
==================
ID | ColumnName
------------------
1 | One_Two_Three
2 | Four_Five_Six
==================
The result I'm after is as follows:
========================
ID | Col1 | Col2 | Col3
------------------------
1 | One | Two | Three
2 | Four | Five | Six
========================
Your assistence is appreciated.
Have a look at this example
DECLARE #Table1 TABLE
([ID] int, [ColumnName] varchar(13))
INSERT INTO #Table1
([ID], [ColumnName])
VALUES
(1, 'One_Two_Three'),
(2, 'Four_Five_Six')
;WITH Vals AS (
SELECT *,
CAST('<d>' + REPLACE([ColumnName], '_', '</d><d>') + '</d>' AS XML) ColumnValue
FROM #Table1
)
SELECT v.*,
A.B.value('.', 'varchar(max)')
FROM Vals v CROSS APPLY
ColumnValue.nodes('/d') A(B)
SQL Fiddle DEMO

T-SQL -- convert comma-delimited column into multiple columns

From the table below, how can I convert the Values column into multiple columns, populated with individual values that are currently separated by commas? Before the conversion:
Name Values
---- ------
John val,val2,val3
Peter val5,val7,val9,val14
Lesli val8,val34,val36,val65,val71,val
Amy val3,val5,val99
The result of the conversion should look like:
Name Col1 Col2 Col3 Col4 Col5 Col6
---- ---- ---- ---- ---- ---- ----
John val val2 val3
Peter val5 val7 val9 val14
Lesli val8 val34 val36 val65 val71 val
Amy val3 val5 val99
First, what database product and version are you using? If you are using SQL Server 2005 and later, you can write a Split user-defined function like so:
CREATE FUNCTION [dbo].[Split]
(
#DelimitedList nvarchar(max)
, #Delimiter varchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(#DelimitedList, DataLength(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
+ #DelimitedList
+ Case When Right(#DelimitedList, DataLength(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
As List
, DataLength(#Delimiter) As DelimiterLen
)
, Numbers As
(
Select TOP (Coalesce(Len(#DelimitedList),1)) Row_Number() Over ( Order By c1.object_id ) As Value
From sys.objects As c1
Cross Join sys.columns As c2
)
Select CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(#Delimiter, CL.list, N.Value + 1)
- ( CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value < Len(CL.List)
And Substring(CL.List, N.Value, CL.DelimiterLen) = #Delimiter
)
You can then split out the values in you want using something akin to:
Select Name, Values
From Table1 As T1
Where Exists (
Select 1
From Table2 As T2
Cross Apply dbo.Split (T1.Values, ',') As T1Values
Cross Apply dbo.Split (T2.Values, ',') As T2Values
Where T2.Values.Value = T1Values.Value
And T1.Name = T2.Name
)
Here is a solution that uses a recursive cte to generate a "table of numbers" (courtesy of Itzik Ben-Gan), which is useful for all manner of problems including string splitting, and PIVOT. SQL Server 2005 onwards. Full table create, insert and select script included.
CREATE TABLE dbo.Table1
(
Name VARCHAR(30),
[Values] VARCHAR(128)
)
GO
INSERT INTO dbo.Table1 VALUES ('John', 'val,val2,val3')
INSERT INTO dbo.Table1 VALUES ('Peter', 'val5,val7,val9,val14')
INSERT INTO dbo.Table1 VALUES ('Lesli', 'val8,val34,val36,val65,val71,val')
INSERT INTO dbo.Table1 VALUES ('Amy', 'val3,val5,val99')
GO
SELECT * FROM dbo.Table1;
GO
WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
SELECT Name, [1] AS Column1, [2] AS Column2, [3] AS Column3, [4] AS Column4, [5] AS Column5, [6] AS Column6, [7] AS Column7
FROM
(SELECT Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY nums.n) AS PositionInList,
LTRIM(RTRIM(SUBSTRING(valueTable.[Values], nums.n, charindex(N',', valueTable.[Values] + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums INNER JOIN dbo.Table1 AS valueTable ON nums.n <= CONVERT(int, LEN(valueTable.[Values])) AND SUBSTRING(N',' + valueTable.[Values], n, 1) = N',') AS SourceTable
PIVOT
(
MAX([VALUE]) FOR PositionInList IN ([1], [2], [3], [4], [5], [6], [7])
) AS Table2
GO
--DROP TABLE dbo.Table1
Which converts this output
Name Values
John val,val2,val3
Peter val5,val7,val9,val14
Lesli val8,val34,val36,val65,val71,val
Amy val3,val5,val99
to
Name Column1 Column2 Column3 Column4 Column5 Column6 Column7
Amy val3 val5 val99 NULL NULL NULL NULL
John val val2 val3 NULL NULL NULL NULL
Lesli val8 val34 val36 val65 val71 val NULL
Peter val5 val7 val9 val14 NULL NULL NULL