Join 2 tables on different columns based on condition - sql

I have two tables, tbl and tbl2 I have to join. Columns of tbl are irrelevant. Here is the tructure of tbl2:
CREATE TABLE tbl2 (
a VARCHAR(10)
, b VARCHAR(10)
, c VARCHAR(10)
, d VARCHAR(10)
, e VARCHAR(10)
);
What I need to do is apply the following join:
if there are several rows with the same a, b and c LEFT JOIN them on a, b, c and d.
else if there are several rows with the same a and b and they are not in the set mentioned above LEFT JOIN on a, b and c
else LEFT JOIN remaining rows from tbl2 on a and b.
I have been thinking what is the best way to achieve the following. tbl2 can be modified since it is only used for the purpose of this query.
Do you have any ideas what is the most efficient way of achieving this?
EDIT:
By 'having the same a, b and c' I meant something like this:
SELECT a, b, c FROM tbl2 GROUP BY a, b ,c HAVING COUNT(*) > 1

WITH tab_a AS
(
SELECT t2.a
, t2.b
, t2.c
, t2.d
, t2.e
, CASE WHEN t1.c = t2.c THEN 1 ELSE 0 END +
CASE WHEN t1.d = t2.d THEN 1 ELSE 0 END AS other_two
FROM tbl t1
LEFT JOIN tbl2 t2
ON t1.a = t2.a
AND t1.b = t2.b
)
SELECT a
, b
, c
, d
, e
FROM tab_a
WHERE other_two = (SELECT MAX(other_two) FROM tab_a);

put these three queries in if else condition. If you want them all then UNION these three.
select *
from tbl t1 left outer join tbl2 t2
on (t1.a=t2.a and t1.b=t2.b and t1.c=t2.c and t1.d=t2.d)
where t2.a=t2.b and t2.b=t2.c;
select *
from tbl t1 left outer join tbl2 t2
on (t1.a=t2.a and t1.b=t2.b and t1.c=t2.c )
where t2.a=t2.b and t2.b<>t2.c;
select *
from tbl t1 left outer join tbl2 t2
on (t1.a=t2.a and t1.b=t2.b)
where t2.a<>t2.b and t2.b<>t2.c;
or try this:
select *
from tbl t1 left outer join tbl2 t2
on (
(t1.a=t2.a and t1.b=t2.b and t1.c=t2.c and t1.d=t2.d and t2.a=t2.b and t2.b=t2.c)
or
(t1.a=t2.a and t1.b=t2.b and t1.c=t2.c and t2.a=t2.b and t2.b<>t2.c)
or
(t1.a=t2.a and t1.b=t2.b and t2.a<>t2.b and t2.b<>t2.c) );

Related

Automatically generate date for insert

I have a group of insert statements below. The only difference between them is the Thru_DT. The first insert is for 6/30/2019 date. The second is for 15 days before that date and the third is for 15 days before the second insert's date. How do I generate these dates automatically without having to hard code them?
INSERT INTO table1 (a, b, c)
SELECT t2.a1, Max(t2.b1) Provider, t2.c1
From table2 t2
LEFT JOIN tb t1 On t2.a = t1.a1
WHERE t1.b Is Null And t2.Thru_DT >= '6/30/2019'
And t2.c In (Select CCN From table3)
INSERT INTO table1 (a, b, c)
SELECT t2.a1, Max(t2.b1) Provider, t2.c1
From table2 t2
LEFT JOIN tb t1 On t2.a = t1.a1
WHERE t1.b Is Null And t2.Thru_DT >= '6/15/2019'
And t2.c In (Select CCN From table3)
INSERT INTO table1 (a, b, c)
SELECT t2.a1, Max(t2.b1) Provider, t2.c1
From table2 t2
LEFT JOIN tb t1 On t2.a = t1.a1
WHERE t1.b Is Null And t2.Thru_DT >= '6/1/2019'
And t2.c In (Select CCN From table3)
You can build the dates in a CTE:
;WITH cte_date(thru_DT)
AS
(
SELECT #GivenDate
UNION ALL SELECT DATEADD(DAY,-15,#GivenDate)
UNION ALL SELECT DATEADD(DAY,-30,#GivenDate)
)
INSERT INTO table1 (a, b, c)
SELECT t2.a1, Max(t2.b1) Provider, t2.c1
From table2 t2
INNER JOIN table3 t3
ON t2.c=t3.CCN
INNER JOIN cte_date d
ON t2.Thru_DT >= d.Thru_DT
LEFT JOIN tb t1 On t2.a = t1.a1
WHERE t1.b Is Null
Use a table constructor:
INSERT INTO table1 (a, b, c)
SELECT t2.a1, Max(t2.b1) Provider, t2.c1
FROM table2 t2 LEFT JOIN
tb t1
ON t2.a = t1.a1 CROSS JOIN
(VALUES ('2019-06-30'), ('2019-06-15'), ('2019-06-01')
) v(Thru_DT)
WHERE t1.b Is Null AND
t2.Thru_DT >= v.Thru_DT AND
t2.c IN (SELECT CCN FROM table3);

How to join two different table using UNION in ms access

I have the below query
Select sum (ABC) as ecr from table1
Where a<>'y' or b is null and c<>'g'
Union all
Select sum(bcd) as ech from table2
Note: I am getting results under one column but I want to be displayed under two columns
Please try this.
SELECT sum (A.ABC) as ecr,SUM(B.bcd) as ech
FROM table1 AS A
LEFT JOIN table2 AS B
ON B.Id = A.Id
Where A.a<>'y' or A.b is null and A.c<>'g'
I think you just need a cross join
Select sum(ABC) as ecr,
sum(bcd) as ech
from table1 t1
cross join table2
Where t1.a <> 'y'
or t1.b is null
and t1.c <> 'g'
In MS Access, you use , for CROSS JOIN:
select t1.ecr, t2.ech
from (select sum(ABC) as ecr
from table1
where a <> 'y' or b is null and c <> 'g'
) t1,
(select sum(bcd) as ech
from table2
) t2;
In any other database, you would use CROSS JOIN.

sql union displays duplicates

image of what I want
I have tried the join on x or y and it didn't work, even the group by didn't work.
What almost gave me the result is the query below
SELECT A.Id ,A.AccNo ,A.Name ,B.Id ,B.AccNo1 ,B.AccNo2 ,B.Name
from Table1 as A
left outer join Table2 as B on A.AccNo = B.AccNo1
union
SELECT A.Id ,A.AccNo ,A.Name ,B.Id, B.AccNo1, B.AccNo2, B.Name,
from Table1 as A
left outer join Table2 as B on A.AccNo = B.AccNo2
After getting the query correct I want to show only the exceptions where there was no link between the tables and its kind of difficult if the T1.ID is repeated
You seem to want a left join:
select t1.*, t2.*
from table1 t1 left join
table2 t2
on t1.id in (t2.accno1, t2.accno2);
Try:
SELECT A.Id ,A.AccNo ,A.Name ,B.Id ,B.AccNo1 ,B.AccNo2 ,B.Name
from Table1 as A
left outer join Table2 as B
ON A.AccNo = (CASE WHEN A.AccNo = B.AccNo1 THEN B.AccNo1 ELSE B.AccNo2 END)
You may nest your original query, and then use max aggregate function with grouping :
SELECT Id ,AccNo ,Name, max(Id2) as Id2, max(Name2) as Name2,
max(AccNo1) as AccNo1, max(AccNo2) as AccNo2
FROM
(
SELECT A.Id ,A.AccNo ,A.Name ,B.Id Id2 ,B.AccNo1 ,B.AccNo2 ,B.Name Name2
from Table1 as A
left outer join Table2 as B on A.AccNo = B.AccNo1
union
SELECT A.Id ,A.AccNo ,A.Name ,B.Id Id2, B.AccNo1, B.AccNo2, B.Name Name2
from Table1 as A
left outer join Table2 as B on A.AccNo = B.AccNo2
) q
GROUP BY Id ,AccNo ,Name;
SQL Fiddle Demo
Do a LEFT JOIN to return the table1 values along with matching table2 values (where t2.accno2 = t1.accno):
select t1.*, t2.*
from table1 t1
left join table2 t2
on t1.accno = t2.accno2
Or, perhaps you want table2 values for matching accno1's as well?
select t1.*, t2.*
from table1 t1
left join table2 t2
on t1.accno in (t2.accno1, t2.accno2)
It this way to resolve:
SELECT
t1.id,
t1.accno,
t1.name,
(
SELECT DISTINCT
id
FROM
table2
WHERE
accno2 = t1.accno
),
(
SELECT DISTINCT
name
FROM
table2
WHERE
accno2 = t1.accno
),
(
SELECT DISTINCT
accno1
FROM
table2
WHERE
accno2 = t1.accno
),
(
SELECT DISTINCT
accno2
FROM
table2
WHERE
accno2 = t1.accno
) FROM
table1 t1
LEFT JOIN table2 t2 ON t1.accno = t2.accno1 OR t1.id = t2.id

Alternative for union in Oracle

Is there any alternate way to fetch the following data without using union?
select A.name,A.age,B.Address,C.phoneNo from table1 A,Table2 B,Table3 C where a.pkId = b.FkId and b.pkId = c.FkId
union
select A.name,A.age,B.Address,C.phoneNo from table4 A,Table5 B,Table3 C where a.pkId = b.FkId and b.pkId = c.FkId
I am using this in Hibernate and unfortunately hibernate doesnt support Union. I was just wondering if there is any other way to achieve it else ill have to write it in a procedure and save the data in temp table and fire a sql to read data from that temp table
There is an alternative for union, but it is not pretty:
select distinct coalesce(x1.name, x2.name) as name,
coalesce(x1.age, x2.age) as age,
coalesce(x1.Address, x2.Address) as age,
coalesce(x1.phoneNo, x2.phoneNo) as age,
from (select A.name, A.age, B.Address, C.phoneNo
from table1 A join
Table2 B
on a.pkId = b.FkId join
Table3 C
on b.pkId = c.FkId
) x1 full outer join
(select A.name, A.age, B.Address, C.phoneNo
from table4 A join
Table5 B
on a.pkId = b.FkId join
Table3 C
on b.pkId = c.FkId
) x2
on 1 = 0; -- always false
I can't imagine why you would want to express a union like this. I would highly recommend, though, that you start using proper, explicit join syntax.
is this working ?
SELECT
CASE DISTINCT_FLG WHEN 1 THEN nameA ELSE nameB END name,
CASE DISTINCT_FLG WHEN 1 THEN ageA ELSE ageB END age,
CASE DISTINCT_FLG WHEN 1 THEN AddressA ELSE AddressB END Address,
CASE DISTINCT_FLG WHEN 1 THEN phoneNoA ELSE phoneNoB END phoneNo
FROM (
SELECT
T1.name AS nameA, T1.age AS ageA, T2.Address AS AddressA, T3.phoneNo AS phoneNoA,
T4.name AS nameB, T4.age AS ageB, T5.Address AS AddressB, T3.phoneNo AS phoneNoB,
ROW_NUMBER() OVER(PARTITION BY T1.name, T1.age, T2.Address, T4.name, T4.age, T5.Address, T3.phoneNo ORDER BY NULL) AS DISTINCT_FLG
FROM
table1 T1,
table2 T2,
table4 T4,
table5 T5,
table3 T3
WHERE
T1.pkId = T2.FkId AND
T4.pkId = T5.FkId AND
(
T2.pkId = T3.FkId OR
T5.pkId = T3.FkId
)
) WHERE DISTINCT_FLG IN (1, 2)
Those two UNION parts have Table3 C in common, so we can join the rest to it. To emulate UNION records from the source table can be replicated through unconditional cross join of an auxiliary table with the required number or rows:
Select Distinct
CASE R.r WHEN 1 THEN A1.name ELSE A2.name END As name ,
CASE R.r WHEN 1 THEN A1.age ELSE A2.age END As age ,
CASE R.r WHEN 1 THEN B1.Address ELSE B2.Address END As Address,
C.phoneNo
From Table3 C, --< Start at common Table3
(Select Rownum r From USER_TABLES Where Rownum < 3) R --< Two rows to replicate Table3 C
-- Any table with more than one row will do
-- USER_TABLES should have enough rows in this particular case
Left Join Table2 B1 On R.r = 1 AND B1.pkId = C.FkId --< Left Join branch one
Left Join table1 A1 On R.r = 1 AND A1.pkId = B1.FkId
Left Join Table5 B2 On R.r = 2 AND B2.pkId = C.FkId --< Left Join branch two
Left Join table4 A2 On R.r = 2 AND A2.pkId = B2.FkId
Where (R.r = 1 AND A1.pkId Is NOT NULL) --/ Make sure we have values
OR (R.r = 2 AND A2.pkId Is NOT NULL) --\ for the branch
But really, consider a view.

Converting a self subquery to a self join

I was wondering if there was a way to convert a self subquery to a self join
Here is the self subquery
SELECT a,
b
FROM c AS t1
WHERE ( b IN (SELECT b
FROM c AS t2
WHERE ( t1.b = b )
AND ( t1.e <> e )) )
If you only want to find the duplicates an EXIST would probably be faster:
SELECT a,b FROM c WHERE EXISTS(SELECT NULL FROM c c2 WHERE c2.b=c.b AND c2.e<>c.e)
If you want to join every record with its duplicate but get only one record for each:
select t1.a
, t1.b
, t1.e as t1e
, t2.e as t2e
from c as t1
inner join c as t2
on t1.b = t2.b
and t1.e > t2.e
(note that i've used > instead of <>)
As e is the Primary Key another way of approaching this would be
SELECT a,
b
FROM (SELECT a,
b,
COUNT(*) OVER (PARTITION BY b) AS Cnt
FROM c) T1
WHERE Cnt > 1
SELECT t1.a, t2.b
FROM c as t1
join c as t2 on t1.b=t2.b
WHERE t1.e <> t2.e
select t1.a
, t1.b
from c as t1
join c as t2
on t1.b = t2.b
and t1.e <> t2.e