Alternative for union in Oracle - sql

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.

Related

Join to only work if a record present in SQL

I have 2 tables - table1 and table2.
In table2, there are multiple records matching criteria for table1 based on:
c_type, h_level, loop, e_id
I want records from RIGHT table ONLY if there is EXACT 1 match. If not, element_nm should be NULL, so I have the exact same records in the output as left table.
SELECT a.*,
b.element_nm
FROM table1 a
LEFT JOIN table2 b ON
a.c_type = b.c_type
AND a.h_level = b.h_level
AND a.loop = b.loop
AND a.e_id = b.e_id
ORDER BY a.file_name,
a.line_num asc;
As this is about one value only, you can use a Sub query in the select clause. Otherwise you'd use a subquery in a LEFT OUTER JOIN or use OUTER APPLY.
SELECT
t1.*,
(
SELECT MIN(t2.element_nm)
FROM table2 t2
WHERE t2.c_type = t1.c_type
AND t2.h_level = t1.h_level
AND t2.loop = t1.loop
AND t2.e_id = t1.e_id
HAVING COUNT(*) = 1
) AS element_nm
FROM table1 t1
ORDER BY t1.file_name, t1.line_num;
Thorsten's answer works when you want only one column from the second table. But if you want multiple columns, it is a bit cumbersome.
Alternatively:
SELECT a.*, b.*
FROM table1 a LEFT JOIN
(SELECT b.*,
COUNT(*) OVER (PARTITION BY b.c_type, b.h_level, b.loop, b.e_id) as cnt
FROM b
) b
ON a.c_type = b.c_type AND
a.h_level = b.h_level AND
a.loop = b.loop AND
a.e_id = b.e_id AND
b.cnt = 1
ORDER BY a.file_name, a.line_num asc;
you should use Row_Number , like this :
WITH cte AS (
SELECT ROW_NUMBER() OVER(PARTITION BY a.c_type ,a.h_level,a.loop ,a.e_id ) rnum
,a.c_type ,a.h_level,a.loop ,a.e_id FROM table1 a
LEFT JOIN table2 b ON a.c_type = b.c_type AND a.h_level = b.h_level AND a.loop = b.loop AND a.e_id = b.e_id
)
,cte2 AS (SELECT * FROM cte WHERE rnum = 2)
SELECT a.*,
CASE WHEN cte2.element_nm IS NULL then b.element_nm ELSE NULL END element_nm
FROM table1 a
LEFT JOIN table2 b ON a.c_type = b.c_type AND a.h_level = b.h_level AND a.loop = b.loop AND a.e_id = b.e_id
LEFT JOIN cte2 ON a.c_type = cte2.c_type AND a.h_level = cte2.h_level AND a.loop = cte2.loop AND a.e_id = cte2.e_id

Select from another table based on condition

I am new to SQL Server and SQL queries. These are my three tables:
Primary keys
Table 1: CaseId
Table 2: ID
Table 3: parentId
In table 3 - if the parent is a father then the status code is 8 and if the parent is a mother then the status code is 10
I want to write a select query where I can get an output like the following:
Please help me, thanks in advance
You must join twice both tables table2 and table3:
select
t1.caseid, t1.caseName,
t3f.name fathername, t3f.email fatheremail,
t3m.name mothername, t3m.email motheremail
from table1 t1
left join table2 t2f on t2f.caseId = t1.caseId
inner join table3 t3f on t3f.parentid = t2f.parentid and t3f.gendercode=8
left join table2 t2m on t2m.caseId = t1.caseId
inner join table3 t3m on t3m.parentid = t2m.parentid and t3m.gendercode=10
See the demo
You should be able to use two sub queries from Table3 where one is for father and the other is for mother. then you can join them with case and case-parent mapping tables to get the desired output,
SELECT
C.CaseID, C.CaseName, F.Name AS FatherName, F.Email AS FatherEmail,
M.Name AS MotherName, M.Email AS MotherEmail
FROM
Table1 C
INNER JOIN
Table2 CP C.CaseID = CP.CaseID
INNER JOIN
(SELECT * FROM TABLE3 WHERE GENDERCODE = 8) F ON F.ParentID = CP.ParentID
INNER JOIN
(SELECT * FROM TABLE3 WHERE GENDERCODE = 10) M ON M.ParentID = CP.ParentID
You can do this with joins but it is a little more complicated than you might expect. A CTE helps:
with p as (
select t2.caseid, t3.*
from table2 t2 join
table3 t3
on t2.parentid = t3.parentid
)
select t1.*,
f.name as fathername, f.email as fatheremail
m.name mothernaem, m.email as motheremail
from table1 t1 left join
p pf
on t1.caseid = pf.caseid and pf.gendercode = 8 left join
p pm
on t1.caseid = pm.caseid and pm.gendercode = 10;
That said, conditional aggregation as proposed by fa06 also works.
you can use conditional aggregation with case when expression
select caseid,casename,max(case when gendercode=8 then name end) as fathername
MAX(case when gendercode=8 then email end) as fatheremail,
MAX(case when gendercode=10 then name end) as mothername,
MAX(case when gendercode=10 then email end) as motheremail
from
(
select b.caseid,c.casesname,gendercode,name,email
from table3 a inner join table2 b on a.parentid=b.parentid
inner join table1 c on b.caseid=c.caseid
)A group by caseid,casename
You can try like following. To combine two rows (mother and father), you can use MAX aggregate function using GROUP BY
SELECT caseid,
casename,
Max(fathername) AS fathername,
Max(fatheremail) AS fatheremail,
Max(mothername) AS mothername,
Max(motheremail) AS motheremail
FROM (SELECT t1.caseid,
t1.casename,
CASE
WHEN gendercode = 8 THEN t3.NAME
ELSE NULL
END AS fathername,
CASE
WHEN gendercode = 8 THEN t3.email
ELSE NULL
END AS fatheremail,
CASE
WHEN gendercode = 10 THEN t3.NAME
ELSE NULL
END AS mothername,
CASE
WHEN gendercode = 10 THEN t3.email
ELSE NULL
END AS motheremail
FROM table1 t1
INNER JOIN table2 t2
ON t1.caseid = t2.caseid
INNER JOIN table3 t3
ON t2.parentid = t2.parentid
WHERE t3.gendercode IN( 8, 10 ))t
GROUP BY caseid,
casename

Postgresql SELECT LEFT JOIN with columns on case

SELECT a1,a2,a3,a4,count(a5),b1,b2,b3
FROM table1
LEFT JOIN table2 ON a1=b1 AND a2=b2 (*here i need to join
next columns a3=b3 only if from table2 will be returned more than 1 records
other wise first 2 columns will be enough*)
group by a1,a2,a3,a4,a5,b1,b2,b3
Anybody knows how to perform this trick ?
Well, if I understand correctly:
FROM table1 t1 LEFT JOIN
(SELECT t2.*, COUNT(*) OVER (PARTITION BY b1, b2) as cnt
FROM table2 t2
)
ON t1.a1 = t2.b1 AND t1.a2 = t2.b2 AND
(cnt = 1 OR t1.a3 = t2.a3)

Compare 2 fields in different rows of the same table

I want to return results from a join where the birth_date of two+ records are the same, but the person_id's are not equal. I am using Oracle. So if I got 4 results where rows 1 & 2 have the same birth_date and different person_id, those rows would be returned. Where rows 3 & 4 have the same birth_date and same person_id, those rows would not be returned. I get results, but I want to filter our results where the birth_date of rows are equal, but the person_id is <>.
select t3.field1, t6.field2, t6.field3, t3.field4, t3.field5
from table1 t1
inner join table2 t2 on t1.#matching = t2.#matching
inner join table3 t3 on t3.#matching = t1.#matching
inner join table4 t4 on t4.#matching = t1.#matching
inner join table5 t5 on t5.#matching = t4.#matching
inner join table6 t6 on t6.#matching = t3.#matching
where t1.#requirement = 'xxx'
and t2.#requirement = 'xxx'
and t2.#requirement is null
and t4.#requirement = 'xxx'
and t5.#requirement = 'xxx'
and t1.#requirement ='xxx'
and t5.#requirement is null
order by t1.#field ASC;
SELECT a.birth_date, a.id, b.id
FROM some_table a, some_table b
WHERE a.birth_date = b.birth_date
AND a.id < b.id
Note the usage of < instead of the intuitive !=. This is done to prevent the same combination returning in different orders (e.g. (1,2) and (2,1)).
Have you tried to group your data by person_id.. This will put all the records with the same person_id into 1 record
The query would look something like this
SELECT a.birth_date, a.id, b.id
FROM some_table a, some_table b
WHERE a.birth_date = b.birth_date
AND a.id < b.id
GROUP BY a.id

Join 2 tables on different columns based on condition

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) );