Oracle sql query union operation? - sql

I have two tables. TableA and TableB. both the tables has some data with two columns as below.
TableA
---------
id Name
--- ----
1 abc
2 def
TableB
---------
id Name
--- ----
1 xyz
2 pqr
Now i would pass list of ids from my application and get same ids along with their names as:
select id, name
from TableA
where id in(1,2)
union select id, name
from TableB
where id in(1,2);
above query gives results as:
1 abc
1 xyz
2 def
2 pqr
But what i need is if same id is present in both the tables then TableB's Name should be considered but not TableA's name.
Expected output:
1 xyz
2 pqr
One more is, if TableB does not contain any data then TableA's data should be fetched.
How can i do that?
Thanks!

Please try using LEFT JOIN.
SELECT TableA.ID,
NVL(TableB.Name, TableA.Name) Name FROM
TableA LEFT JOIN TableB
ON TableA.ID=TableB.ID
WHERE TableA.ID IN (1, 2)

Try this query using simple union you can club the records
SELECT id, name from tableA where id not in (SELECT id FROM tableB)
UNION ALL
SELECT id, name from tableB

Try this:
select id, name from TableA where id in(1,2) and id not in ( select id from TableB) a union select id, name from TableB where id in(1,2);

try this
SELECT id , name from (
select id, name from TableA where id in(1,2)
union select id, name from TableB where id in(1,2)) t
GROUP BY id;

Use Union All, and exists/not exists to control which results are returned from table_a based on the existence of any records in table_b
select id,name
from (
select id,name
from table_b
union all
select id,name
from table_a
where exists (select null from table_b) and
not exists (
select null
from table_b
where table_b.id = table_a.id)
union all
select id,name
from table_a
where not exists (select null from table_b))
where id in (1,2)
http://sqlfiddle.com/#!4/36f26/3

one way of doing it if rows can be in A, A+B or B is (if tablea always has data, then techdo's answer is better):
select id, name
from (select id, name, row_number() over (partition by id order by rnk) rn
from (select id, name, 1 rnk from tableb
union all
select id, name, 2 rnk from tablea))
where rn = 1;

SELECT (case when B.id = A.id then b.id else a.id end) as id,
(case when B.id = A.id then b.name else a.name end) as name
FROM tableA a left JOIN tableB b ON (a.id = b.id)

MINUS operator - returns only unique rows returned by the first query but not by the second:
WITH tab_a AS
(
SELECT 1 id, 'abc' val FROM dual
UNION
SELECT 2, 'def' FROM dual
),
tab_b AS
(
SELECT 1, 'xyz' val FROM dual
UNION
SELECT 2, 'pqr' FROM dual
)
-- This is your query --
SELECT * FROM tab_b
MINUS
SELECT * FROM tab_a
/
ID VAL
----------
1 xyz
2 pqr

Following code is for selecting data from both the tables(A +B) and then taking out data using minus and join for the rows not required. Code can be easily modified if the requirement changes from selecting names from table A instead of table B.
select * from tableA where id in (1,2)
union
select * from tableB where id in (1,4)
minus
select a,id, a.Name from tableA a join tableB b on a.id = b.id where
a.id in (1,2);

Related

Get exclusive users in each table

I have 4 tables as shown below
For each table I want the count of users that are present exclusively in that table (not present in other tables). The result should look something likes this
I have one way of getting desired result as shown below:
First Column:
SELECT COUNT(DISTINCT A.id) table1_only
FROM table1 A
LEFT JOIN (SELECT DISTINCT id
FROM table2
UNION
SELECT DISTINCT id
FROM table3
UNION
SELECT DISTINCT id
FROM table4) B
ON A.id = B.id
WHERE B.id IS NULL
Second Column:
SELECT COUNT(DISTINCT A.id) table2_only
FROM table2 A
LEFT JOIN (SELECT DISTINCT id
FROM table1
UNION
SELECT DISTINCT id
FROM table3
UNION
SELECT DISTINCT id
FROM table4) B
ON A.id = B.id
WHERE B.id IS NULL
Third Column:
SELECT COUNT(DISTINCT A.id) table3_only
FROM table3 A
LEFT JOIN (SELECT DISTINCT id
FROM table1
UNION
SELECT DISTINCT id
FROM table2
UNION
SELECT DISTINCT id
FROM table4) B
ON A.id = B.id
WHERE B.id IS NULL
Fourth Column:
SELECT COUNT(DISTINCT A.id) table4_only
FROM table4 A
LEFT JOIN (SELECT DISTINCT id
FROM table1
UNION
SELECT DISTINCT id
FROM table2
UNION
SELECT DISTINCT id
FROM table3) B
ON A.id = B.id
WHERE B.id IS NULL
But I wanted to know if there is any efficient and scalable way to get same result. Just for 4 tables the amount of code is too much.
Any ways of optimizing this task will be really helpful.
Sample fiddle. (This fiddle is for mysql, I am looking for a generic SQL based approach than any db specific approach)
P.S.:
There is no complusion on the result needs to be in column wise. It can be row wise as well, as shown below:
I would approach this by combining the data from all tables. Then aggregate and filter:
select which, count(*) as num_in_table_only
from (select id, min(which) as which, count(*) as cnt
from ((select id, 1 as which from table1) union all
(select id, 2 as which from table2) union all
(select id, 3 as which from table3) union all
(select id, 4 as which from table4)
) t
group by id
) i
where cnt = 1
group by which
Note: In your sample data, the ids are unique in each table. This solution assumes that is true, but can easily be tweaked to handle duplicates within a table.

Is there a way to JOIN a table consisting of two tables UNIONed together?

SELECT
a.First_Name
,b.Last_Name
FROM table a
JOIN table b
UNION table c
ON a.ID=b.ID
I'm not even sure how the syntax would be for this. Basically what I want is to UNION table b and c then JOIN the new table to table a.
You can join a subquery:
SELECT
a.First_Name,
bc.Last_Name
FROM a
JOIN
(
SELECT ID, Last_Name FROM b
UNION
SELECT ID, Last_Name FROM c
) bc ON bc.ID = a.ID;
Make the union a subquery:
SELECT
a.First_Name
,bc.Last_Name
FROM table a
JOIN (
SELECT * FROM table b
UNION
SELECT * FROM table c
) AS bc
ON a.ID=bc.ID
Note that the subquery must have its own alias (bc in this case) which should be referenced in the join clause and the select (meaning you can't reference tables b or c directly outside of the subquery).
The result of a union can be join to others table if you the the proper join clause
You could use this sintax
select a.col1, t.name
from table3 c
inner join (
select id, name1 as name
from table1 a
union id, name2
from table2 b
) t on. t.id = a.id

SQL Server Custom sorting in ORDER BY clause

ALTER PROCEDURE sp_1
AS
BEGIN
SELECT B.ID, 'B' AS Name
FROM tableB inner join table a inner join table c
UNION
SELECT A.ID, 'A' AS NAME
FROM tableA inner join table b inner join table c
UNION
SELECT C.ID, 'C' AS NAME
FROM tableC inner join table a inner join table b
ORDER BY CASE WHEN Name = 'A' THEN '1'
ELSE Name END ASC
END
The code above returns invalid column name 'Name'. Is there anyway where i can sort 'name customly by using order by without delcaring any new paramters.
SELECT B.ID, 'B' AS Name, 1 as ExplicitOrderGroup
FROM tableB inner join table a inner join table c
UNION
SELECT A.ID, 'A' AS NAME, 0 as ExplicitOrderGroup
FROM tableA inner join table b inner join table c
UNION
SELECT C.ID, 'C' AS NAME, 1 as ExplicitOrderGroup
FROM tableC inner join table a inner join table b
ORDER BY ExplicitOrderGroup, Name
Note, UNION (which behaves a bit like DISTINCT) will not collaps now same rows with different ExplicitOrderGroup. You have to determine whethere it's OK or you need some other behaviour.
You can't use alias in order by you have to use concrete column but you can achieve this easily using common table expression for example
WITH MY_CTE (col1, col2, col3)
AS
-- Define the CTE query.
(
SELECT col1, prop2 as col2, col3
FROM table
)
-- Define the outer query referencing the CTE name.
SELECT col1, col2, col3
FROM MY_CTE
ORDER BY col2
GO
This is just an example to point u in direction
Can achieve this by sub-query without any new variable/parameter
ALTER PROCEDURE sp_1
AS
BEGIN
SELECT SQ.ID, SQ.Name FROM (
SELECT B.ID, 'B' AS Name
FROM table B inner join table a inner join table c
UNION
SELECT A.ID, 'A' AS NAME
FROM table A inner join table b inner join table c
UNION
SELECT C.ID, 'C' AS NAME
FROM table C inner join table a inner join table b
) SQ
ORDER BY CASE WHEN SQ.Name = 'A' THEN '1'
ELSE SQ.Name END ASC
END
In this case I would add an extra column for priority.
Like this:
ALTER PROCEDURE sp_1
AS
BEGIN
SELECT B.ID, 'B' AS Name, 2 AS prio
FROM tableB inner join table a inner join table c
UNION
SELECT A.ID, 'A' AS NAME, 1 AS prio
FROM tableA inner join table b inner join table c
UNION
SELECT C.ID, 'C' AS NAME, 2 AS prio
FROM tableC inner join table a inner join table b
ORDER BY prio ASC, Name ASC
END

Table Join condition

I have two tables
Table A :
Item Lookup
A X
B null
C Y
D K
Table B :
Lookup
X
Y
Z
I want to join these tables and get output like
Item Lookup
A X
B null
C Y
I wanna pick up all matching lookup and as well as null lookups in my output view. can anyone tell me the join condition
It looks like you want rows in A that either match B or have a NULL lookup. You would do this with an inner join and a special condition:
select distinct a.item, a.lookup
from tableA a join
tableB b
on (a.lookup = b.lookup) or (a.lookup is NULL);
If you have an index on lookup, then exists is better for performance:
select a.item, a.lookup
from tableA a
where a.lookup is null or
exists (select 1 from tableB b where b.lookup = a.lookup);
EDIT:
Using a left join with a where condition is also possible:
select distinct a.item, a.lookup
from tableA a left join
tableB b
on a.lookup = b.lookup
where (a.lookup is NULL and b.lookup is NULL) or
b.lookup is not null;
The following query will return all records of TableA that have Lookup in TableB, or have a NULL Lookup:
SELECT Item, Lookup
FROM TableA
WHERE (Lookup IS NULL) OR Lookup IN (SELECT Lookup FROM TableB)
I think this will help
SELECT DISTINCT A.Item, A.Lookup FROM A, B WHERE A.lookup=B.lookup OR A.lookup IS NULL
You can do also do it via a left outer join:
with tablea as (select 'A' item, 'X' lookup from dual union all
select 'B' item, null lookup from dual union all
select 'C' item, 'Y' lookup from dual union all
select 'D' item, 'K' lookup from dual),
tableb as (select 'X' lookup from dual union all
select 'Y' lookup from dual union all
select 'Z' lookup from dual)
select a.item,
a.lookup
from tableA a
left outer join tableB b on (a.lookup = b.lookup)
where a.lookup is null
or a.lookup = b.lookup;
ITEM LOOKUP
---- ------
A X
C Y
B

compare two tables on SQL server with result to show which column from its associated table

I need to compare two tables on SQL server.
table1
id value
3 5
table2
id value
8 6
SELECT *
FROM
(
SELECT a.id AS a_id, a.value AS a_value
FROM [table1] as a
UNION ALL
SELECT b.id AS b_id, b.value AS b_value
FROM [table2] as b
) tmp
GROUP BY tmp.a_id, tmp.a_value
HAVING COUNT(*) = 1
ORDER BY tmp.a_id
I get result:
a_id a_value
3 5
8 6
I need to know which column is from which table, e.g.
a_id a_value b_id b_value
3 5 8 6
Any help would be appreciated !
Thanks !
What about:
SELECT *
FROM (
SELECT a.id, a.value, 'Table1' AS TableName
FROM [table1] as a
UNION ALL
SELECT b.id, b.value, 'Table2' AS TableName
FROM [table2] as b
) tmp
GROUP BY tmp.id, tmp.value, tmp.TableName
HAVING COUNT(*) = 1
ORDER BY tmp.id
You could add a column for tbl. Something like:
SELECT
m.id
,m.value
INTO #tmp
FROM
(
SELECT a.id AS id, a.value AS value
FROM [table1] as a
UNION ALL
SELECT b.id AS id, b.value AS value
FROM [table2] as b
) m
GROUP BY
m.id
,m.value
HAVING COUNT(*) = 1
CREATE CLUSTERED INDEX ix_tmpidvalue ON #tmp
(
id ASC
,value ASC
)
SELECT *
FROM
(
SELECT a.id AS id, a.value AS value, 'a' tbl
FROM [table1] as a
UNION ALL
SELECT b.id AS id, b.value AS value, 'b' tbl
FROM [table2] as b
) tmp
WHERE EXISTS
(
SELECT 1
FROM #tmp n
WHERE tmp.id = n.id
AND tmp.value = n.value
)
ORDER BY tmp.id