Oracle SQL: Exclude IDs from another table without subquery join - sql

I would like to know if the following is possible without joining the same table twice:
Table A:
+----+------+
| ID | ColA |
+----+------+
| 1 | A1 |
| 2 | A2 |
| 3 | A3 |
| 4 | A4 |
+----+------+
Table B:
+----+------+
| ID | ColB |
+----+------+
| 1 | B1 |
| 2 | B2 |
| 3 | B3 |
| 4 | B4 |
| 5 | B5 |
| 6 | B6 |
+----+------+
Table C:
+----+
| ID |
+----+
| 1 |
| 2 |
+----+
Desired result: (A LEFT JOIN B WITHOUT C)
+----+------+------+
| ID | ColA | ColB |
+----+------+------+
| 3 | A3 | B3 |
| 4 | A4 | B4 |
+----+------+------+
So basically I need to add Column B to Table A, hence left join, and exclude all IDs which occur in Table C.
Current solution:
SELECT a.id, a.ColA, b.ColB
FROM tableA a
LEFT JOIN tableB b ON a.id = b.id
WHERE a.id NOT IN(
SELECT a2.id FROM tableA a2
LEFT JOIN tableC c on a2.id = c.id)
What's irritating me is, that the exclusion of table C requires an additional left join of table A with table C. Isn't there a more straight-forward approach, without having to join table A again as part of the subquery, if all I want to do is to exclude IDs which occur in table C from the resultset?.
Thanks

Use a not exists:
SELECT a.id, a.ColA, b.ColB
FROM tableA a
LEFT JOIN tableB b ON a.id = b.id
where not exists(select 1 from tablec c where a.id = c.id)
The issue with using a not in with a select in Oracle is that:
a) it has to return the whole subquery dataset
b) if there are nulls, it breaks
TOM link regarding these 2 issues

won't this work?
SELECT a.id, a.ColA, b.ColB
FROM tableA a
JOIN tableB b ON a.id = b.id
WHERE a.id NOT IN (SELECT c.Id FROM tableC c)
this can also be done in a join
SELECT a.id, a.ColA, b.ColB
FROM tableA a
JOIN tableB b ON a.id = b.id
LEFT JOIN tableC C ON a.id = c.id
WHERE c.Id is null

Related

Join two tables, get all results from first table

So I have two tables. Let's say this is the structure of the tables:
TABLE A | TABLE B |
-----------------------------------------|
ID | div | ID | date | val |
-----------------------------------------|
A | a | A | d1 | 22 |
B | b | B | d2 | 10 |
C | c | C | d3 | 9 |
F | l | F | d1 | 10 |
What I want is to have this result:
row 1: A, a, null, null
row 2: B, b, d2, 10
row 3: C, c, null, null
row 4: F, l, d1, 10
It's a left JOIN but I want to retrieve only the values that have val = 10.
I am using SQLite. I have tried many answers from different threads without any success.
You are looking for left join:
select a.*, b.date, b.val
from a left join
b
on a.id = b.id and b.val = 10
order by a.id;

where col1 from tab1 and col1 from tab2 in select from tab3

I wanna gets some records from table A and table B, they don't have any common records but my select is depends on from table C which have keys to A and B, for example:
I have 3 tables: A, B and C, something like that:
A
+----+-------+
| ID | NAME |
+----+-------+
| 1 | nameA |
+----+-------+
B
+----+-------+
| ID | NAME |
+----+-------+
| 1 | nameB |
+----+-------+
C
+-----+-----+-------+
| IDA | IDB | NAMEC |
+-----+-----+-------+
| 1 | 1 | nameC |
+-----+-----+-------+
and i wanna get sth like this:
+-------+-------+
| NameA | NameB |
+-------+-------+
| nameA | nameB |
+-------+-------+
so I am trying with:
select a.name, b.name
from tableA a, tableB b
join (select idA, idB
from tableC where nameC='nameC') tmp
on tmp.idA=a.id and tmp.idB=b.id
but its not working
This is just two joins and filtering:
select a.name as nameA, b.name as nameB
from tableC c join
tableA a
on c.idA = a.id join
tableB b
on c.idB = b.id
where c.nameC = 'nameC';
Your code does not work because you are mixing archaic join syntax (commas) with proper, explicit, standard, readable JOIN syntax. Never use commas. Always use JOIN.
The reason your code does not work is because of the scoping rules around commas. The ON clause does not recognize the first table reference.

Join on two tables gives duplicate results

i have a table with data that I want to join unto another table. Problem is that the join can happen on two columns of the same table, where I want to get the first join to work and if this Fails i want the second join to give me a valid result.
Base table:
| ID1 | ID2 | Value |
| a1 | a2 | val_1 |
| b1 | b2 | val_2 |
| c1 | c2 | val_3 |
join Table:
| ID1 | ID2 | Join_Value |
| | a2 | join_val_1 |
| b1 | | join_val_2 |
| c1 | c2 | join_val_3 |
What i tried was this:
select base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
The Result is this:
| ID1 | ID2 | Value | Join_Value |
| a1 | a2 | val_1 | join_val_1 |
| b1 | b2 | val_2 | join_val_2 |
| c1 | c2 | val_3 | join_val_3 |
| c1 | c2 | val_3 | join_val_3 |
What i want is this:
| ID1 | ID2 | Value | Join_Value |
| a1 | a2 | val_1 | join_val_1 |
| b1 | b2 | val_2 | join_val_2 |
| c1 | c2 | val_3 | join_val_3 |
I hope i made my Problem clear.
You don't need to join the same table twice. Just specify the condition in the ON
select b.ID1, b.ID2, b.[Value], j.Join_Value
from [base] b
inner join [join] j on b.ID1 = j.ID1
or (
j.ID1 = ''
and b.ID2 = j.ID2
)
You are going to get duplicate rows for for the c1 and c2 rows because they match on both of your Join table joins (j1 and j2).
A quick fix is to add a DISTINCT to your query:
select DISTINCT base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value
from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
A better fix, depending on your DBMS is to use a window function:
select id1, id2, Value, Join_Value
FROM (
select base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value,
ROW_NUMBER() OVER(
PARTITION BY base.id1, base.id2 -- Group rows based on (id1, id2) combination
ORDER BY j1.id1 -- If more than one row, give priority to row with "id1" value
) AS RowNum
from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
) src
WHERE RowNum = 1 -- Only return one row
This will make sure you always one row maximum per (id1, id2) combination.
Try:
select *
from base b
join [join] j on b.id1 = j.id1 or b.id2 = j.id2
First, your version does exactly what you want. Here is a db<>fiddle.
Second, for more control over the matching, you can use a lateral join. This allows you to choose only one matching row -- say the one where both ids match:
select b.id1, b.id2, b.value, jt.join_value
from base b cross apply
(select top (1) jt.*
from jointable jt
where b.id1 = jt.id1 or
b.id2 = jt.id2
order by (case when b.id1 = jt.id1 then 1 else 0 end) +
(case when b.id2 = jt.id2 then 1 else 0 end) desc
) jt ;

A table with 2 foreign keys referencing the same table [duplicate]

This question already has an answer here:
2 Foreign Keys referencing the same Primary Key in MySQL
(1 answer)
Closed 4 years ago.
I have a table with 2 forign keys referencing the same table. How would like print out the join contents? I'm using SQLite
TableA
S.NO Col1 Col2
1 | a | b
2 | f | g
3 | d | e
TableB
S.NO Col3
a | apple
b | ball
f | frog
g | grape
d | dog
e | eat
How do i print out the following?
S.NO |Col1 | Col2 | Col3| Col4
1 | a |apple | b | ball
2 | f |frog | g | grape
3 | d |dog | e | eat
I tried
SELECT TableA.Col1, TableB.Col3, TableA.Col1
FROM TableA, TableB
WHERE TableA.Col1 = TableB.Col3
AND TableA.Col2 = TableB.Col3
Thanks for your help.
Two inner joins will do:
select
a.sno, a.col1, b1.col3, a.col2, b2.col3
from tablea a
join tableb b1 on b1.sno = a.col1
join tableb b2 on b2.sno = a.col2
Note tableb is joined twice. In this case you need to give it a different alias each time: b1 and b2. Otherwise there would be no clear way of referencing columns from each one.

Single SQL query to check if either table contains a row with column=x

I have 2 unrelated tables A and B which both have foreign key constraints on C. I need to run an sql query that determines if either A or B contain a given id of C. My first approach was to use the union all but A and B are not related thus it will not work.
Any ideas?
Select 1
From DUAL
Where Exists ( Select null From Table_A Where a.fk = :id ) OR
Exists ( Select null From Table_B Where b.fk = :id );
You could indeed use union, why not? But why use UNION ALL, and not just UNION? Just pick the one common column:
SELECT 1
FROM
(select A.fk from A inner join C on A.FK = C.pk
UNION
select B.fk from B inner join C on B.FK = C.pk) AS bothTables
WHERE fk = 'desiredValue';
This would work just nicely.
Tested it on the following tables in MySQL, with myValue = 1, just to verify.
mysql> select * from A;
+------+--------+------+
| pk | value | fk |
+------+--------+------+
| 1 | ape | 2 |
| 2 | fjfjfj | 3 |
+------+--------+------+
2 rows in set (0.00 sec)
mysql> select * from B;
+------+--------+------+
| pk | value | fk |
+------+--------+------+
| 1 | katt | 1 |
| 2 | fjfjfj | 3 |
+------+--------+------+
2 rows in set (0.00 sec)
mysql> select * from C;
+------+-------+
| pk | value |
+------+-------+
| 1 | hei |
| 2 | nei |
| 3 | jeg |
+------+-------+
3 rows in set (0.00 sec)
Not 100% sure what you're asking but if you mean to return the elements of A and B that match the id in C then this will do it.
Select c.*, a.*, b.*
From c.id
Left Outer Join a On a.id = c.id
Left Outer Join b On b.id = c.id
Where c.id = #somevalue and (a.id Is Not Null or b.id Is Not Null)
Where #somevalue is the value you're looking for.