Joins Related Query - sql

I have 2 tables A and B.
A has a field say ID,Name.
B has two fields say ID1 & ID2.
In B the data for both the ID's comes from A's ID.
How can I join the two tables such that I get the corresponding name for the two ID's.
E.g
ID1 ID2 ID1NAME ID2NAME
1 2 X Y
3 4 F G
Regards,

Join TableA twice on both Id1 and Id2 from TableB.
SELECT b.Id1
,b.Id2
,a1.Name AS Id1Name
,a2.Name AS Id2Name
FROM TableB b
JOIN TableA a1 ON a1.Id = b.Id1
JOIN TableA a2 ON a2.Id = b.Id2
May want to left join depending on if Ids are not always present in TableA.

Related

Issue combining inner and outer joins in Oracle

I'm trying to simplify a query against an Oracle database that uses multiple inner joins against the same table. I know the multiple joins against the same table can be cleaned up with outer joins, but I'm failing to elicit the same results.
Below are the tables listing only the columns needed for the query. The "link" column in table X maps to the ID, ID2, and ID3 columns in tables A, B, and C respectively.
**Table A**
id
**Table B**
id
id2
nm
**Table C**
id3
nm
**Table X**
cfgnum
link
Existing (working query):
SELECT COUNT(*)
FROM A,
X,
X X1,
X X2,
B,
C
WHERE X.cfgnum = 9999
AND A.id = X.link
AND X.cfgnum = X1.cfgnum
AND X.cfgnum = X2.cfgnum
AND B.id2 = X1.link
AND B.id = A.id
AND C.id3 = X2.link
AND C.nm = B.nm;
The below was my attempt to rewrite, but it returns substantially different results. With the comments in place it does return the right number of rows against the linking X table.
SELECT COUNT(*)
FROM X
LEFT JOIN A ON X.link = A.id
LEFT JOIN B on X.link = B.id2 --AND A.id = B.id
LEFT JOIN C on X.link = C.id3 --AND B.nm = C.nm
WHERE cfgnum = 9999
AND COALESCE (A.id, B.id2, C.id3) IS NOT NULL;
Any help is appreciated!
UPDATE
Apologies, new at this, and it appears I did not provide enough information. Table X groups rows from other tables under a "cfgnum". Each "cfgnum" can link to numerous rows in numerous tables.
Hopefully the below sample data makes sense.
**Table A**
id
1
2
7
**Table B**
id id2 nm
3 1 name1
4 3 name2
**Table C**
id3 nm
5 name2
6 name3
**Table X**
cfgnum link
9998 1
9998 2
9998 3
9999 1
9999 3
9999 4
9999 6
9999 7
9999 8
My thought was using left joins to X would minimize the number of joins required to X. Notice the "working" query has X three times in the from clause. If I'm offbase, please let me know.
Thanks again.
There are no left joins in your previous query. So you can try this -
SELECT COUNT(*)
FROM A
JOIN X ON A.id = X.link
JOIN X1 ON X.cfgnum = X1.cfgnum
JOIN X2 ON X.cfgnum = X2.cfgnum
JOIN B ON B.id2 = X1.link
AND B.id = A.id
JOIN C ON C.id3 = X2.link
AND C.nm = B.nm
WHERE X.cfgnum = 9999
Updated:
If you have just one-to-one relationship, ie you have just 1 link to each of A,B,C for specific cfgnum, you can use aggregation:
SELECT COUNT(*)
FROM (select cfgnum,
max(decode(rownum,1,link)) link1,
max(decode(rownum,2,link)) link2,
max(decode(rownum,3,link)) link3
from X
WHERE cfgnum = 9999
group by cfgnum -- just in case if you need to remove the predicate "cfgnum=9999"
) x1
join A on A.id in (link1,link2,link3)
join B on B.id2 in (link1,link2,link3) and B.id = A.id
join C on C.id3 in (link1,link2,link3) and C.nm = B.nm;

How to combine two tables with different number of columns SQL

I have two tables A and B which look like this:
Table A
col1 col2 ID
--------------
a b 1
c d 2
Table B
col3 col4 ID
--------------
x t 1
y u 1
z o 2
m n 2
I want to create this new table:
Table C
col1 col2 col3 col4 ID
-------------------------
a b x t 1
a b y u 1
c d z o 2
c d m n 2
The ID's in A are not the unique ID's of those elements in that list. I just need to add a few more properties from their "parent" table(B) in this new table. I know there will be a lot of repetitions but I don't mind that for now.
I tried this following statement:
INSERT INTO C(SELECT A.*, B.col1, B.col2 from A LEFT OUTER JOIN B ON (B.ID = A.ID));
But it is resulting in something I did not expect. The row count of C must be the same as the row count of B. However when I used this SQL query, the resulting tables row count was even more than the total row count of A and B. I'm a beginner in using SQL I would appreciate any help.
A basic JOIN will do:
select
a.col1,
a.col2,
b.col3,
b.col4,
a.id
from tablea a
join tableb b on a.id = b.id
If I got you right your tables do not have any relation to each other.
So in this case you should be able to do something like this?
Select * from table1, table2
You can also combine this with an insert so you should get your new table

Fetching fields with specific criteria - Oracle

I am trying to extract particular data from 2 tables based on specific criteria. But the result is not as expected. Can someone please help?
Criteria:
Need to fetch id pairs whose type is A alone.
Tables:
Table A
ID1 ID2
579643307310619501 644543316683180704
296151129721950503 328945291791563504
Table B
ID TYPE
579643307310619501 A
579643307310619501 B
579643307310619501 C
644543316683180704 A
296151129721950503 A
328945291791563504 A
Expected Result:
ID1 ID2
296151129721950503 328945291791563504
(Since only this pair is of type A alone, individually)
Note: The IDs, ID1 and ID2 both must be present in ID field of Table B.
What I've tried:
SELECT id1, id2
FROM A
JOIN B ON A.id1 = B.id
WHERE A.id1 IN (SELECT id FROM B)
AND A.id2 IN (SELECT id FROM B)
AND B.type='A'
GROUP BY id1, id2
HAVING count(*)=1;
In the approach below, I use a CTE to first identify all ID values having exclusively the 'A' type. Then I join TableA to this CTE, twice, to filter off any records either of whose ID1 or ID2 values are not in the exclusively 'A' type list.
WITH cte (ID) AS (
SELECT ID
FROM TableB
GROUP BY ID
HAVING SUM(CASE WHEN TYPE <> 'A' THEN 1 ELSE 0 END) = 0
)
SELECT a.ID1, a.ID2
FROM TableA a
INNER JOIN cte t1
ON a.ID1 = t1.ID
INNER JOIN cte t2
ON a.ID2 = t2.ID;
Find below a working demo (for SQL Server - I can't get Oracle to work anywhere).
Demo
Here is an Oracle solution using the MINUS operator.
The top sub-query gets the set of records where both ID1 and ID2 are of type 'A'. The bottom sub-query gets the set of records where either ID1 or ID2 is not of type 'A'. The result is the set of records in the top set which are not also in the bottom set.
select a.id1, a.id2
from a
join b b1 on b1.id = a.id1
join b b2 on b2.id = a.id2
where b1.type = 'A'
and b2.type = 'A'
minus
select a.id1, a.id2
from a
join b b1 on b1.id = a.id1
join b b2 on b2.id = a.id2
where b1.type != 'A'
or b2.type != 'A'
/
This SQL Fiddle demo returns the right row but there's a bit of a problem with its display: for some reason the numbers are rounded down.
Note on performance
This hits table A twice and table B four times. With small tables and a well-sized buffer cache this is not so important.
#TimBiegeleisen uses the WITH clause and that approach only hits each table once. However, Oracle will materialize the CTE as a temporary table. The overhead of doing this for such small amounts of data makes his solution consistently slower than mine. Including an /*+ inline */ hint in the CTE projection prevents Oracle from materializing the temporary table and the performance of the two queries becomes comparable.
However, if the tables become large enough there will be a point at which the WITH clause approach with a materialized temporary table is the more performative approach. As always with query tuning, the specifics matter greatly and benchmarking is the key to success.
Here is a sample for Oracle and my solution.
This is valid for any letter, A, B, C... If you want only for A, add an additional filter in the where of the main query.
create table a (id1 number,id2 number, constraint pk_a primary key(id1,id2));
create table b (id number, type char(1), constraint pk_b primary key(id,type));
insert into a values(57,64);
insert into a values(29,32);
insert into b values(57,'A');
insert into b values(57,'B');
insert into b values(57,'C');
insert into b values(64,'A');
insert into b values(29,'A');
insert into b values(32,'A');
commit;
select a.*
from a, b b1, b b2
where a.id1 = b1.id
and a.id2 = b2.id
and b1.type = b2.type
and not exists (select null
from b b1bis
where b1bis.id = b1.id
and b1.type <> b1bis.type)
and not exists (select null
from b b2bis
where b2bis.id = b2.id
and b2.type <> b2bis.type);

SQL - not sure how to join tables

I'm trying to join two tables like this:
Table A
ID Value1
1 A
2 B
3 C
Table B
ID Value2
1 A
3 B
4 C
Result should be:
ID Value1 Value2
1 A A
2 B null
3 C B
4 null C
I.e. join Table A to Table B on ID. If ID doesn't exist in Table A, add the ID from Table B.
The closest I've come is:
SELECT
a.ID, a.Value1, b.Value2
FROM
TableA a
OUTER JOIN
TableB b ON a.ID = b.ID
That gives me the new rows from TableB, but the ID is null.
How can I accomplish this?
You are very close, you just need a little push in the right direction:
SELECT COALESCE(a.ID, B.ID) As ID, a.Value1, b.Value2
FROM TableA a
FULL OUTER JOIN TableB b ON a.ID=b.ID
The COALESCE function returns the first parameter it gets that is not null. since this is a full outer join, a.id will be null on one row and b.id would be null on a different row.
Try this:
SELECT *
FROM TableA A
FULL OUTER JOIN TableB B
ON A.ID = B.ID;
Just a note: you should not name your tables in SQL with spaces in them.
Remember the basic for joining different tables
SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name=table2.column_name;
For your case:
SELECT a.value1, b.value2
FROM TableA a
FULL OUTER JOIN TableB b ON a.ID=b.ID
remember full outer join
The FULL OUTER JOIN keyword returns all rows from the table (tableA) and from the table (tableB) and the FULL OUTER JOIN keyword combines the result of both LEFT and RIGHT joins.

Query to check the consistency of records

I have four tables
TableA:
id1
id2
id3
value
TableB:
id1
desc
TableC:
id2
desc
TableD:
id3
desc
What I need to do is to check if all combinations of id1 id2 id3 from table B C and D exist in the TableA. In other words, table A should contain all possible combinations of id1 id2 and id3 which are stored in the other three tables.
This query evaluates all combinations of id1, id2 and id3 (the cross join) and finds which combinations are not present in table a.
select b.id1, c.id2, d.id3 from
TableB b cross join TableC c cross join TableD d WHERE NOT EXIST
(select 1 FROM TableA a WHERE a.id1=b.id1 AND a.id2=c.id2 AND a.id3=d.id3)
EDIT: With an RIGHT JOIN
SELECT allPerms.id1, allPerms.id2, allPerms.id3 FROM a RIGHT JOIN (select b.id1, c.id2, d.id3 from
TableB b cross join TableC c cross join TableD) allPerms
ON a.id1=allPerms.id1 AND a.id2=allPerms.id2 AND a.id3=allPerms.id3
WHERE a.id1 IS NULL
The two are pretty much the same. Since we are not actually fetching values from the joined table, some people prefer the first approach, since it captures the intent and spirit of the query. The second version is more "implementation oriented". A good optimizer will produce an efficient plan for both, but on some lesser RDBMSs, the second version will run faster.
With a predefined set for table D - id3 has values (2,5,6)
select b.id1, c.id2 from
TableB b cross join TableC c WHERE NOT EXIST
(select 1 FROM TableA a WHERE a.id1=b.id1 AND a.id2=c.id2 AND a.id3 IN (2,5,6))
But, this doesn't give you the id3 that is missing in the table A row. For that, I think the simplest is to emulate the table via a union, e.g.
select b.id1, c.id2, d.id3 from
TableB b, TableC c, (select 2 id3 union select 5 union select 6) d
WHERE NOT EXIST
(select 1 FROM TableA a WHERE a.id1=b.id1 AND a.id2=c.id2 AND a.id3=d.id3)
(This is still using cross join - it's implied if tables are separated by commas.)