Run a query on Parent to show records of only one child table - sql

I've 3 tables, viz A, B & c.
B & C has forign key of A.
Now I want to run a query on A, in such a way, that only records of B are returned.
That is, I want to exclude all the results of C and show only results of B, when a query is executed on all the records of A.
Hope, I've the question makes sense.

If you want to return all records of A and any matching records from B then something a left outer join is appropriate:
SELECT a.*, b.*
FROM a, b
WHERE a.id = b.id
This will return each record from A and populate values from B where there is a match. This will also return multiple rows for records that occur in A if there are multiple rows in B that match.
Just because there is a foreign key in C that references something in table A, it won't be returned unless you use it in your query.
If you just want to return all records from B when the foreign key appears in A then maybe you want:
SELECT *
FROM B
WHERE B.id in (SELECT id FROM A)
AND B.id not in (SELECT id FROM C)
or
SELECT *
FROM B
WHERE EXISTS (SELECT 1 FROM A JOIN B on A.id = B.id)
AND NOT EXISTS (SELECT 1 FROM C JOIN B on C.id = B.id)
All these assume that id is the key which is common.

Is this what you want?
select a.*
from a
where exists (select 1 from b where b.aid = a.aid) and
not exists (select 1 from c where c.aid = c.aid);

Related

Subqueries vs Multi Table Join

I've 3 tables A, B, C. I want to list the intersection count.
Way 1:-
select count(id) from A a join B b on a.id = b.id join C c on B.id = C.id;
Result Count - X
Way 2:-
SELECT count(id) FROM A WHERE id IN (SELECT id FROM B WHERE id IN (SELECT id FROM C));
Result Count - Y
The result count in each of the query is different. What exactly is wrong?
A JOIN can multiply the number of rows as well as filtering out rows.
In this case, the second count should be the correct one because nothing is double counted -- assuming id is unique in a. If not, it needs count(distinct a.id).
The equivalent using JOIN would use COUNT(DISTINCT):
select count(distinct a.id)
from A a join
B b
on a.id = b.id join
C c
on B.id = C.id;
I mention this for completeness but do not recommend this approach. Multiplying the number of rows just to remove them using distinct is inefficient.
In many databases, the most efficient method might be:
select count(*)
from a
where exists (select 1 from b where b.id = a.id) and
exists (select 1 from c where c.id = a.id);
Note: This assumes there are indexes on the id columns and that id is unique in a.

SQL Get rows that doesn't appear in another table

I have this SQL problem: I have tables A and B. Table A has columns id and name, Table B amount and id which is a foreign key to table A.id.
I need to return all table A rows that don't have their id stored in table B. Any ideas?
So the complete opposite is:
SELECT *
FROM a
LEFT OUTER JOIN b ON a.id = b.id;
Here row what I need is left out of result
Just add a where clause:
SELECT a.*
FROM a LEFT OUTER JOIN
b
ON a.id = b.id
WHERE b.id IS NULL;
You can also use NOT EXISTS:
select a.*
from a
where not exists (select 1 from b where b.id = a.id);
In most databases, the two methods typically have similar performance.

SQL Insert into table A from table B based off table C

I have an empty table that I would like to fill with rows from a second table, based off a third table, Ill call them A,B,C respectively.
Table C has ID numbers that match ID numbers for rows in Table B. For every ID in table C, I want to add the corresponding row from table B into Table A.
This is what I have, and I am getting an error saying that I cannot use the last statement.
INSERT INTO TABLEA
SELECT * FROM TABLEB
WHERE ID FROM TABLEB = ID FROM TABLEC;
DSNT408I SQLCODE = -199, ERROR: ILLEGAL USE OF KEYWORD FROM. TOKEN ( . AT
MICROSECONDS MICROSECOND SECONDS SECOND MINUTES MINUTE WAS EXPECTED
DSNT418I SQLSTATE = 42601 SQLSTATE RETURN CODE
Any help would be appreciated.
INSERT INTO TableA
SELECT B.*
FROM TableB AS B
JOIN TableC AS C ON B.ID = C.ID
Or possibly that will give you too many duplicates (if there are multiple rows in C that match a given row in B), in which case you might need:
INSERT INTO TableA
SELECT B.*
FROM TableB AS B
WHERE B.ID IN (SELECT C.ID FROM TableC AS C)
Or:
INSERT INTO TableA
SELECT DISTINCT B.*
FROM TableB AS B
JOIN TableC AS C ON B.ID = C.ID
Both of those give you one row in A for each row in B that matches one or more rows in C.
How would I add a WHEN clause to this? Let's say Table C has another column called VALUE, and I want to add all the ID numbers that have a value of 'x' or greater. How would I do that, I tried adding JOIN TableC AS C ON B.ID = C.ID AND C.VALUE > 5 but I still got all the values from TABLE C.
Working with the first query (fixing the others being left as an 'exercise for the reader'), then what I think you should be doing is just:
INSERT INTO TableA
SELECT B.*
FROM TableB AS B
JOIN TableC AS C ON B.ID = C.ID
WHERE C.Value > 5
The optimizer should translate that to an equivalent expression:
INSERT INTO TableA
SELECT B.*
FROM TableB AS B
JOIN TableC AS C ON B.ID = C.ID AND C.Value > 5
I'm not clear from your comment whether you somehow added a second reference to TableC in the one query, or you modified your query as shown in this second example. If you were not using LEFT JOIN anywhere, then adding the AND C.Value > 5 term to the ON clause or as a WHERE clause should have yielded the correct data.
When debugging this sort of problem, it is worth noting that this INSERT statement has a perfectly good SELECT statement in it that you can run on its own to review what is going to be added to TableA. You might want to augment the select-list to include (at least) C.ID and C.Value just to make sure nothing is going haywire.

Join and showing different columns from tables

I have a simple SQL question, I thought it would be quite straight forward but have got myself in a muddle. Any help would be appreciated
I have table A which contains a last updated
Table A has a one to many with Table B
Table B has a one to many with Table C
I want to show all rows of table C with the last updated time from table A. I have tried some joins but they dont seem to be quite working. Ideally I want somehting like
select a.lastUpdated c.* from TableA a, TableC c where
a.id in (select a_id from TableB where (select b_id from TableC where c_id = select
id from TableC where XXXX=YYYY))
so I can pass in an id for table C and then get one row returned with the last updated time present.
XXX=YYY would be my criteria for returning one row of table C.
Any help or pointers appreciated
Thanks
Something like
SELECT c.*
FROM TableA AS a
INNER JOIN TableB AS b
ON a.a_id = b.b_id
INNER JOIN TableC AS c
ON b.b_id = c.c_id
WHERE a.lastUpdated = c.lastUpdated;
Should work. This is a situation where a striaght INNER JOIN should suffice; unless of course I have missed something.
I hope this helps.
You should be able to do this by joining A and B together, aggregating the results at the c_id level, and then joining in C:
select tc.*, maxlastupdated
from tablec tc left outer join
(select tb.c_id, max(lastupdated) as maxlastupdated
from tablea ta join
tableb tb
on ta.b_id = tb.b_id
group by ta.id
) ta
on tc.c_id = ta.c_id
You need to drive your SQL query from Table C.
The query below displays the updated timestamp column from table A.
Since it is a one-to-many in the direction of tables A --> B --> C
You will inevitably end-up with a lot of rows in table C - all with the same timestamp.
SELECT c.*, a1.update_timestamp
FROM table_c c, table_b b, table_a a1
WHERE c.join_column = b.join_column
AND b.join_column = a1.join_column
AND a1.update_timestamp =
(SELECT max(a2.update_timestamp) FROM table_a a2
WHERE a2.<identifying columns> = a1.<identifying columns>
);

Is there alternative way to write this query?

I have tables A, B, C, where A represents items which can have zero or more sub-items stored in C. B table only has 2 foreign keys to connect A and C.
I have this sql query:
select * from A
where not exists (select * from B natural join C where B.id = A.id and C.value > 10);
Which says: "Give me every item from table A where all sub-items have value less than 10.
Is there a way to optimize this? And is there a way to write this not using exists operator?
There are three commonly used ways to test if a value is in one table but not another:
NOT EXISTS
NOT IN
LEFT JOIN ... WHERE ... IS NULL
You have already shown code for the first. Here is the second:
SELECT *
FROM A
WHERE id NOT IN (
SELECT b.id
FROM B
NATURAL JOIN C
WHERE C.value > 10
)
And with a left join:
SELECT *
FROM A
LEFT JOIN (
SELECT b.id
FROM B
NATURAL JOIN C
WHERE C.value > 10
) BC
ON A.id = BC.id
WHERE BC.id IS NULL
Depending on the database type and version, the three different methods can result in different query plans with different performance characteristics.