SQL: Single select query from 2 Non-Joining tables - sql

I have 2 tables which doesn't have any references to each other and I'm trying to create a third table (for reference lookup) by selecting from fields from both tables.
TableA has an A_ID
TableB has a B_ID
I want to create Table C which has a 1 to 1 reference between A_ID to B_ID
where A_ID = FirstID and B_ID = SecondID, I can't join the 2 tables because there's nothing in common.
Something like:
Insert INTO [TableC]
(FirstID, SecondID)
SELECT
A_ID As FirstID,
(Select B_ID From TableB)
FROM TableA
Basically we are creating a relationship right now with Table C so that we can use it to reference the two tables in the future using the their IDs.

Assuming TableA and TableB truly have nothing in common, then what you want is the Cartesian product, that is, for every row in A times every row in B.
INSERT INTO TableC(FirstID,SecondID)
SELECT A_ID,B_ID
FROM TableA
CROSS JOIN TableB
Perhaps what you really want is to join them by ROW_NUMBER().
INSERT INTO TableC(FirstID,SecondID)
SELECT A_ID,B_ID
FROM (SELECT A_ID,ROW_NUMBER() OVER (ORDER BY whatever) as rownumA FROM TableA) a
FULL OUTER JOIN (SELECT B_ID,ROW_NUMBER() OVER (ORDER BY whatever) as rownumB FROM TableB) b ON a.rownumA=b.rownumB

Related

Optimizing sql query: check for all rows in table B if any rows in table C reference the same row in table A

I have 3 tables, A, B and C structured like this
CREATE TABLE a (
id SERIAL NOT NULL PRIMARY KEY
);
CREATE TABLE b (
id SERIAL NOT NULL PRIMARY KEY,
a_id INT REFERENCES a(id) ON DELETE CASCADE
);
CREATE TABLE c (
id SERIAL NOT NULL PRIMARY KEY,
a_id INT REFERENCES a(id) ON DELETE CASCADE
);
Where the relationships are many-to-one. What i want is, for every row in table b, i want to check if any row in table c has a reference to the same row in table a. Now, I already have the query
SELECT
b.id,
true
FROM
b
WHERE EXISTS (
SELECT 1
FROM c
WHERE b.a_id = c.a_id
)
UNION
SELECT
b.id,
false
FROM
b
WHERE NOT EXISTS (
SELECT 1
FROM c
WHERE b.a_id = c.a_id
)
ORDER BY id
Though I am not certain, I think this is doing double work, and going through the table twice, and I am wondering how I could optimize it to only traverse the table once.
Is it possible with a simple query, or do I have to do anything complex?
Simply move the EXISTS clause into your SELECT clause.
SELECT
b.id,
EXISTS (SELECT null FROM c WHERE c.a_id = b.a_id) AS c_exists
FROM b;
The same with an IN clause, which I prefer for being even a tad simpler:
SELECT
id,
a_id IN (SELECT c.a_id FROM c) AS c_exists
FROM b;
This can be done with a subquery, a left join, and a case.
The subquery gets you a list of distinct c.a_id values.
SELECT DISTINCT a_id FROM c;
Then do this
SELECT b.id,
CASE WHEN distinct_ids.a_id IS NULL THEN 'false'
ELSE 'true' END has_c_row
FROM b
LEFT JOIN (
SELECT DISTINCT a_id FROM c;
) distinct_ids ON b.a_id = distinct_ids.a_id
This shape of query is called an antijoin or IS NULL ... LEFT JOIN. It detects the rows in the first table that don't match rows in the second table.
The subquery gives us a view of the data in table c with at most one row per each distinct a_id value. Without the subquery, we might get duplicate rows in the result query.
This eliminates your WHERE EXISTS correlated subqueries; even though PostgreSQL's query planner is pretty smart, sometimes it does the slow thing with subqueries like that.
If it is still too slow for you, create these indexes on the a_id columns.
ALTER TABLE b ADD INDEX a_id (a_id);
ALTER TABLE c ADD INDEX a_id (a_id);
i think i understand what you are after
this is how i would do it
SELECT b.id, ISNULL(res.result,0) as result
FROM b
LEFT JOIN (
SELECT c.id, 1 as result
FROM c
INNER JOIN a on a.id = c.id
) res on b.id = res.id
i dont think you need to worry about distinct if they are all unique ids

Select Statement that orders depending on existence of foreign key in other table

I have two tables a and b where b contains a foreign key fk_a_id to table a id column.
Now I would like to select on table a but order the result depending on whether table b has a foreign key entry for it or not. the rows where table b does not have an entry for it should come first.
I haven't tried much yet, besides a join, which is probably not even the right direction.
select a.*
from a as a
join b as b on b.fk_a_id = a.id
order by id desc
One method is a left join. But that could duplicate rows if b contains multiple instances of a given key.
Another method uses logic in the order by:
select a.*
from a
order by (case when not exists (select 1 from b where b.fk_a_id = a.id) then 1 else 2 end),
id desc;
For performance, you would want an index on b(fk_a_id).

DB2 How to insert values into a table based on 2 other tables?

I have 3 tables:
Table A : obj_1 (varchar), rlt(varchar), obj_2 (varchar)
Table B : r_id (int), r_obj (varchar)
Table C: obj1 (int), action(varchar), obj2(int)
I need to insert into Table C so it is the exact copy of Table A, except instead of the obj_1 and obj_2 names, it uses the reference number for that object (r_id) from Table C.
INSERT into tablec (obj1, action, obj2) ((select r_id from tableb, tablea
where tablea.obj_1 = tableb.r_obj), (select rlt from tablea), (select r_id
from tableb, tablea where tablea.obj_1 = tableb.r_obj))
You can use an insert-select statement with a query that joins reference and project:
INSERT INTO tablec (obj1, action, obj2)
(SELECT b1.r_id, a.action, b2.r_id
FROM tablea a
JOIN tableb b1 ON a.obj1 = b1.r_obj
JOIN tableb b2 ON a.obj2 = b2.r_obj)
I had to use a workaround since db2 has certain limitations:
I used 2 temporary views as stepping stones to my goal.
I do the 2 joins required separately:
create view temp1 (a, b) as (select tablea.p_id, tableb.r_id from tablea
tableb where tablea.obj_1 = tableb.r_obj)
create view temp2 (c, d, rlt) as (select tablea.p_id, tableb.r_id, tablea.rlt
from tablea, tableb where tablea.obj_2 = tableb.r_obj)
then I used one insert statament to combine these 2 based on the PK from tablea
INSERT INTO final (obj_1, rlt, obj_2)
(select temp1.b, temp2.rlt, temp2.d from temp1 join temp2 on temp1.a = temp2.c)

Is there any better way to write this query

I designed below query for my delete operation. I am new to SQL and just wanted to check with experienced people here if it is fine or any better way to do this. I am using DB2 database
DELETE FROM TableD
WHERE B_id IN
(
SELECT B.B_id
FROM TableB tB
INNER JOIN TableA tA
ON tB.A_id = tA.A_id
WHERE A_id = 123
) AND
C_id IN (1,2,3)
This has two IN clause which I am little worried and not sure if I could use EXISTS clause anywhere.
Database Structure as below:
Table A has ONE TO MANY relation with Table B
Table B has ONE TO MANY relation with Table C
Table B has ONE TO MANY relation with Table D
Table D has composite primary key ( B_id, C_id )
Table D data somewhat similar to below
B_id|C_id
----------
1 | 1
1 | 2
1 | 3
2 | 4
2 | 5
3 | 5
Here I have to delete rows which have C_id in array of values. But since the index is a composite of B_id and D_id, I am retrieving related B_id to the particular entity of Table A by equality operator A_id=123
There isn't necessarily anything wrong with your method. However, a useful alternative technique to know is merge:
merge into TableD
using (
select distinct
B.B_id
from TableB tB
inner join TableA tA on
tB.A_id = tA.A_id and
A_id = 123
) AB
on
TableD.B_id = AB.B_id and
C_id in (1,2,3)
when matched then delete;
Note that I had to use distinct on the inner query to prevent duplicate matches.
You can use merge like this too :
merge into TableD
using TableB tB
on B.B_id = TableD.B_id
and tB.A_id in (select A_id from TableA tA where A_id = 123)
and C_id in (1,2,3)
when matched then delete;
DELETE FROM TableD tD
WHERE
EXISTS (
SELECT
tB.B_id
FROM
TableB tB
WHERE
A_id = 123
AND tB.B_id = tD.B_id
)
AND C_id IN (1, 2, 3)

How to get a row from a Table with no ids

I have two tables, table A and table B, table A has and Id,a,b and c rows , Table B just has a,b and c and what i want (but i dont know how to do it) is to get a single row from Table B
(i would be something like comparinga,b,and c at the same time)
thanks!
SELECT A.id, B.a, B.b, B.c
FROM A
JOIN B
ON(A.a=B.a)
AND(A.b=B.b)
AND(A.c=B.c)
But I'd change your database structure and add foreign keyto table B referencing table A. It would really help you with this and later cases.
May I suggest changing your table structure.
TableA ( id primary key)
TableB(colA,colB,colC,fid foreign key refrencing TableA.id )
So now you dont have to store colA,colB,colC values in both TableA and TableB
Select B.*
From TableA A
Join TableB B
On A.id=B.fid