Comparing multiples tables on basis of single column postgresql - postgresql

I have 3 tables table1,table2,table3.Each having same structure.I want to compare their rows on basis of one column.
structure of tables
table1
country_code country_name rate vendor
91 india 2.0 abc
92 pak 1.0 abc
table2
country_code country_name rate vendor
91 india 2.1 abc1
92 pak 1.1 abc1
93 afgan 1.1 abc1
table3
country_code country_name rate vendor
91 india 2.2 abc2
92 pak 1.2 abc2
93 afgan 1.2 abc2
880 bang 1.2 abc2
desired output is
country_code country_name rate vendor rate vendor rate vendor
91 india 2.0 abc 2.1 abc1 2.2 abc2
92 pak 1.0 abc 1.1 abc1 1.2 abc2
93 afgan 1.1 abc1 1.2 abc2
880 bang 1.2 abc2
I tried full outer join but did not get desired result.
I used this query
SELECT *
FROM table1 a
FULL OUTER JOIN table2 b ON a.country_code=b.country_code
FULL OUTER JOIN table3 c ON c.country_code=a.country_code ;
and result of above query is
91 india 2 91 india 2.1 91 india 2.2
92 pak 1 92 pak 1.1 92 pak 1.2
93 afgan 1.1
880 bang 1.2
93 afgan 1.2
but I want it like
91 india 2 91 india 2.1 91 india 2.2
92 pak 1 92 pak 1.1 92 pak 1.2
93 afgan 1.1 93 afgan 1.2
880 bang 1.2

this should work:
select * from
(
select distinct t3.country_code,
t3.country_name,
t1.rate,
t1.vendor,
t2.rate,
t2.vendor,
t3.rate,
t3.vendor
from (select * from table1 t1 union
select * from table2 t2 union
select * from table3 t3
) allk left join table1 t1 on allk.country_code = t1.country_code
left join table2 t2 on allk.country_code = t2.country_code
left join table3 t3 on allk.country_code = t3.country_code
) a
order by case
when a.country_code like '9%' then 1
else 2
end nulls last;

Related

Unpivot in postgres with a column created in the same query

I am trying to unpivot a table with PostgreSQL as described here.
My problem is that I am creating a new column in my query which I want to use in my cross join lateral statement (which results in an SQL error because the original table does not have this column).
ORIGINAL QUESTION:
select
"Name",
case
when "Year"='2020' then "Date"
end as "Baseline"
from "test_table"
EDIT: I am using the example from the referred StackOverflow question:
create table customer_turnover
(
customer_id integer,
q1 integer,
q2 integer,
q3 integer,
q4 integer
);
INSERT INTO customer_turnover VALUES
(1, 100, 210, 203, 304);
INSERT INTO customer_turnover VALUES
(2, 150, 118, 422, 257);
INSERT INTO customer_turnover VALUES
(3, 220, 311, 271, 269);
INSERT INTO customer_turnover VALUES
(3, 320, 211, 171, 269);
select * from customer_turnover;
creates the following output
customer_id q1 q2 q3 q4
1 100 210 203 304
2 150 118 422 257
3 220 311 271 269
3 320 211 171 269
(I used the customer_id 3 twice because this column is not unique)
Essentially, what I would like to do is the following: I would like to calculate a new column qsum:
select customer_id, q1, q2, q3, q4,
q1+q2+q3+q4 as qsum
from customer_turnover
and use this additional column in my unpivoting statement to produce the following output:
customer_id turnover quarter
1 100 Q1
1 210 Q2
1 203 Q3
1 304 Q4
1 817 qsum
2 150 Q1
2 118 Q2
2 422 Q3
2 257 Q4
2 947 qsum
3 220 Q1
3 311 Q2
3 271 Q3
3 269 Q4
3 1071 qsum
3 320 Q1
3 211 Q2
3 171 Q3
3 269 Q4
3 971 qsum
As I do not want to have qsum in my final output, I understand that I cannot use it in my select statement, but even if I would use it like this
select customer_id, t.*, q1, q2, q3, q4,
q1+q2+q3+q4 as qsum
from customer_turnover c
cross join lateral (
values
(c.q1, 'Q1'),
(c.q2, 'Q2'),
(c.q3, 'Q3'),
(c.q4, 'Q4'),
(c.qsum, 'Qsum')
) as t(turnover, quarter)
I receive the following SQL error: ERROR: column c.qsum does not exist
How can I produce my desired output?
Not sure to well understand your issue, maybe a subquery can help :
select s.baseline
from
( select
"Name",
case
when "Year"='2020' then "Date"
end as "Baseline"
from "test_table"
) AS s

Getting duplicate records from 2 sql tables

I have 2 SQL tables
Table #1
account
product
expiry-date
101
prod1
2021-01-30
102
prod2
2021-02-20
103
prod3
2021-03-09
103
prod3
2021-03-19
104
prod4
2021-03-15
105
prod5
2021-04-23
105
prod5
2021-04-24
106
prod6
2021-04-25
Table #2
account
101
106
From the above 2 tables I want to get only unmatched records from Table1 and avoid duplicate records.
Result:
account
product
expiry-date
102
prod2
2021-02-20
103
prod3
2021-03-09
104
prod4
2021-03-15
105
prod5
2021-04-23
Below query I tried but I am getting duplicate records, because expiry date is unique on account. I am getting below records in my output
SQL query I tried:
select distinct (a.account, a.product, a.expiry-date)
from table1 a
where a.account not in (select account from table2)
Result:
account
product
expiry-date
102
prod2
2021-02-20
103
prod3
2021-03-09
103
prod3
2021-03-19
104
prod4
2021-03-15
105
prod5
2021-04-23
105
prod5
2021-04-24
You can use the same query using aggregation:
SELECT a.account
,a.product
,MIN(a.expiry) expiry
FROM table1 a
WHERE a.account NOT IN (
SELECT account
FROM table2
)
GROUP BY a.account
,a.product
You can use an anti-join and then ROW_NUMBER() For example:
select *
from (
select a.*, row_number() over(partition by accoun order by expiry) as rn
from table1 a
left join table2 b on b.account = a.account
where b.account is null
) x
where rn = 1

PostgreSQL : comparing two sets of results does not work

I have a table that contains 3 columns of ids, clothes, shoes, customers and relates them.
I have a query that works fine :
select clothes, shoes from table where customers = 101 (all clothes and shoes of customer 101). This returns
clothes - shoes (SET A)
1 6
1 2
33 12
24 null
Another query that works fine :
select clothes ,shoes from table
where customers in
(select customers from table where clothes = 1 and customers <> 101 ) (all clothes and shoes of any other customer than 101, with specified clothes). This returns
shoes - clothes(SET B)
6 null
null 24
1 1
2 1
12 null
null 26
14 null
Now I want to get all clothes and shoes from SET A that are not in SET B.
So (example) select from SET A where NOT IN SET B. This should return just clothes 33, right?
I try to convert this to a working query :
select clothes, shoes from table where customers = 101
and
(clothes,shoes) not in
(
select clothes,shoes from
table where customers in
(select customers from table where clothes = 1 and customers <> 101 )
) ;
I tried different syntaxes, but the above looks more logic.
Problem is I never get clothes 33, just an empty set.
How do I fix this? What goes wrong?
Thanks
Edit , here is the contents of the table
id shoes customers clothes
1 1 1 1
2 1 4 1
3 1 5 1
4 2 2 2
5 2 3 1
6 1 3 1
44 2 101 1
46 6 101 1
49 12 101 33
51 13 102
52 101 24
59 107 51
60 107 24
62 23 108 51
63 23 108 2
93 124 25
95 6 125
98 127 25
100 3 128
103 24 131
104 25 132
105 102 28
106 10 102
107 23 133
108 4 26
109 6 4
110 4 24
111 12 4
112 14 4
116 102 48
117 102 24
118 102 25
119 102 26
120 102 29
122 134 31
The except clause in PostgreSQL works the way the minus operator does in Oracle. I think that will give you what you want.
I think notionally your query looks right, but I suspect those pesky nulls are impacting your results. Just like a null is not-NOT equal to 5 (it's nothing, therefore it's neither equal to nor not equal to anything), a null is also not-NOT "in" anything...
select clothes, shoes
from table1
where customers = 101
except
select clothes, shoes
from table1
where customers in (
select customers
from table1
where clothes = 1 and customers != 101
)
For PostgreSQL null is undefined value, so You must get rid of potential nulls in your result:
select id,clothes,shoes from t1 where customers = 101 -- or select id...
and (
clothes not in
(
select COALESCE(clothes,-1) from
t1 where customers in
(select customers from t1 where clothes = 1 and customers <> 101 )
)
OR
shoes not in
(
select COALESCE(shoes,-1) from
t1 where customers in
(select customers from t1 where clothes = 1 and customers <> 101 )
)
)
if You wanted unique pairs you would use:
select clothes, shoes from t1 where customers = 101
and
(clothes,shoes) not in
(
select coalesce(clothes,-1),coalesce(shoes,-1) from
t1 where customers in
(select customers from t1 where clothes = 1 and customers <> 101 )
) ;
You can't get "clothes 33" if You are selecting both clothes and shoes columns...
Also if u need to know exactly which column, clothes or shoes was unique to this customer, You might use this little "hack":
select id,clothes,-1 AS shoes from t1 where customers = 101
and
clothes not in
(
select COALESCE(clothes,-1) from
t1 where customers in
(select customers from t1 where clothes = 1 and customers <> 101)
)
UNION
select id,-1,shoes from t1 where customers = 101
and
shoes not in
(
select COALESCE(shoes,-1) from
t1 where customers in
(select customers from t1 where clothes = 1 and customers <> 101)
)
And Your result would be:
id=49, clothes=33, shoes=-1
(I assume that there aren't any clothes or shoes with id -1, You may put any exotic value here)
Cheers

How to get those rows where all the values are same against unique id

I have below mentioned table:
ID State City Pincode Code Date
U-1 AAB CCV 141414 121 2018-04-04 18:08:17
U-1 AAB CCV 141414 121 2018-04-04 18:08:17
U-2 BTB ERV 150454 145 2018-05-05 19:11:25
U-2 BTB ERV 150454 145 2018-05-05 19:11:25
U-3 FFT ERT 160707 150 2018-05-22 21:37:45
U-4 FFT RTT 160707 150 2018-05-28 14:23:48
I want to fetch only those rows where all the values are same in the particular unique ID.
Output:
ID State City Pincode Code Date
U-1 AAB CCV 141414 121 2018-04-04 18:08:17
U-1 AAB CCV 141414 121 2018-04-04 18:08:17
U-2 BTB ERV 150454 145 2018-05-05 19:11:25
U-2 BTB ERV 150454 145 2018-05-05 19:11:25
Get the duplicate rows and join the result to the original table.
select * from table a
join ( select id,state,city,pincode,code,date
from table
group by id,state,city,pincode,code,date
having count(*) > 1 ) b
on a.id = b.id
and a.state = b.state
and a.city = b.city
and a.pincode = b.pincode
and a.code = b.code
and a.date=b.date
You can try this:
SELECT * FROM table WHERE ID IN (
SELECT count(*) AS c FROM table
WHERE c > 1
GROUP BY ID
)
Get all rows where count of the records with this ID is greater than 2 (at least two rows with this id)

T-SQL - Several groupings with computation

I need assistance on how I can come up with a query wherein the Table 1 and table 2 will be joined to perform calculation. I have 'cursor' in mind but I am having trouble on conceptualizing. Some kickstart will be a huge help.
basically what I need is something like this:
Table 1:
Rep_Date NumID NumValue Score Period
1/10/2015 1 161 4 Q1
1/11/2015 1 167 2 Q1
1/12/2015 1 95 1 Q1
1/01/2016 1 150 1 Q2
1/02/2016 1 100 2 Q2
1/03/2016 1 600 5 Q2
1/10/2015 38 1 1 Q1
1/11/2015 38 1 2 Q1
1/12/2015 38 1 2 Q1
1/01/2016 38 1 1 Q2
1/02/2016 38 1 2 Q2
1/03/2016 38 1 4 Q2
1/10/2015 113 5 3 Q1
1/11/2015 113 2 4 Q1
1/12/2015 113 8 1 Q1
1/01/2016 113 11 4 Q2
1/02/2016 113 1 5 Q2
1/03/2016 113 5 3 Q2
Table 2
NumID CalculationType
1 SUM
38 SUM
113 AVG
Expected Result:
Rep_Date NumID Result Period
1/10/2015 1 7 Q1
1/01/2016 1 8 Q2
1/10/2015 38 5 Q1
1/01/2016 38 7 Q2
1/10/2015 113 2.67 Q1
1/01/2016 113 4 Q2
Join by NumID to get the calculation type.
Use 'Score' field to derive the result.
Group by NumID, Period
This should work:
declare #t1 table (Rep_Date varchar(50), numId int, numValue int, score int, period varchar(50));
insert into #t1 values
('1/10/2015','1','161','4','Q1'),
('1/11/2015','1','167','2','Q1'),
('1/12/2015','1','95','1','Q1'),
('1/01/2016','1','150','1','Q2'),
('1/02/2016','1','100','2','Q2'),
('1/03/2016','1','600','5','Q2'),
('1/10/2015','38','1','1','Q1'),
('1/11/2015','38','1','2','Q1'),
('1/12/2015','38','1','2','Q1'),
('1/01/2016','38','1','1','Q2'),
('1/02/2016','38','1','2','Q2'),
('1/03/2016','38','1','4','Q2'),
('1/10/2015','113','5','3','Q1'),
('1/11/2015','113','2','4','Q1'),
('1/12/2015','113','8','1','Q1'),
('1/01/2016','113','11','4','Q2'),
('1/02/2016','113','1','5','Q2'),
('1/03/2016','113','5','3','Q2');
declare #t2 table (numId int, CalculationType varchar(50));
insert into #t2 values
('1' ,'SUM'),
('38' ,'SUM'),
('113' ,'AVG')
select
min(t.rep_date) Rep_Date
,t.numId
,case when max(t2.CalculationType) = 'SUM' then sum(t.score)
when max(t2.CalculationType) = 'AVG' then round(avg(1.0 * t.score), 2)
else 0
end Result
,t.period
from
#t1 t
join #t2 t2 on t.numId = t2.numId
group by
t.period, t.numId
order by
numId, period
Output