How to find rows missing by every group - sql

I have two tables:
Input:
A:
ID col
1 a
1 b
1 c
2 a
2 b
3 x
4 y
B
ID col
1 a
1 b
2 a
I want to for every ID in B, find rows in A but not in B by every ID.
Output:
ID Col
1 c
2 b
What I tried:
left/right join. I am trying something like select * from a left join b on a.id = b.id where b.id is null
except. select * from a except select * from b
but not sure how to modify it.

Assuming you want the values in A for which there are records in B with the same ID, but not the same col, you could do:
select
a.ID,
a.col
from A
left join B
on b.ID = a.ID and b.col = a.col
where A.ID in (select distinct ID from B) -- B contains this `ID` somewhere...
and B.ID is null -- ...but not with the same `col`
Test it here.

Using a combination of exists and not exists.
select *
from a
where exists (select 1 from b where a.id=b.id) --id check
and not exists (select 1 from b where a.id=b.id and a.col=b.col) -- col check

Related

SQL: Left joining optional column should show in the table but with different row

Imagine an application form with different sections: A, B, C and D.
B should be based on A.
C should be based on B, but is optional.
Can proceed to D even if C is null but applicant will have less options in the future -- so this will be related to B and C.
A day after, the applicant will edit Form C. This data should not be attached to the existing D since the options are now different.
So there are now 2 scenarios/applications:
Applicant has Section D with no Section C
Applicant has Section C related to B, but should proceed with different Section D.
Here are my pseudo tables. I want to join them and should show the optional scenario.
[Table A]
id date
1 07-11-2021
[Table B]
id date A_id
1 07-11-2021 1
[Table C]
id date B_id
1 07-12-2021 1
[Table D]
id date B_id C_id (optional)
1 07-11-2021 1 null
I would like to get the following table
[Forms table]
A_id B_id C_id D_id
1 1 null 1
1 1 1 null
Here is what I tried
SELECT
A.ID as A_id,
B.ID as B_id,
C.ID as C_id,
D.ID as D_id
FROM A
LEFT JOIN B ON B.A_id = A.ID
LEFT JOIN C ON C.B_id = B.ID
LEFT JOIN D ON D.B_id = B.ID
But the result has only 1 row
[Forms table]
A_id B_id C_id D_id
1 1 1 1
How can I separate the 2 scenarios?
A simple method is to add a condition to your ON clause for table D:
LEFT JOIN D ON D.B_id = B.ID AND C.ID IS NULL
If you want two null rows in case you don't find neither C nor D, use UNION ALL instead:
SELECT A.ID as A_id, B.ID as B_id, C.ID as C_id, NULL as D_id
FROM A
LEFT JOIN B ON B.A_id = A.ID
LEFT JOIN C ON C.B_id = B.ID
UNION ALL
SELECT A.ID as A_id, B.ID as B_id, NULL as C_id, D.ID as D_id
FROM A
LEFT JOIN B ON B.A_id = A.ID
LEFT JOIN D ON D.B_id = B.ID

Sum query with reference to different coloumn for each row?

Is it possible to carry out a sum query where the row for each part of the sum is determine from a join?
For example if I have tables
table A
id | value
1 | 10
2 | 15
3 | 10
And
table b
id | b | c
1 | 2 | 3
2 | 1 | 2
Is it possible to do a SUM(tableA.value * tableB.<specific_column>) Where either the SUM is carried out directly as a join or the join table is prequired from a specification, for sake of argument, a string "bcb"?
Edit:
The end result I'm hoping to achieve would be equivalent to this:
SUM(SELECT value * b FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 1,
SELECT value * c FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 2,
SELECT value * b FROM tableA a JOIN tableB b ON b.id = 2 WHERE a.id = 3);
I guess there's two parts to this: A simple join of A and selected values from B such that B is reduced to a single selectValue column.
Thanks.
As asked in comment it should be better to show us what output you really wants, but as I understand you wants to do something like :
SELECT id, SUM(a.value * b.b)
FROM a JOIN b USING(id)
GROUP BY id;
It's what you want ? I do not really understand you "bcb" point ...
Not because in your comment you said SUM(value, value, value) and I think you want to add those values so, I'll do something like this :
WITH
sum1 AS (SELECT value * b AS res
FROM tableA a
JOIN tableB b ON b.id = 1
WHERE a.id = 1),
sum2 AS (SELECT value * c AS res
FROM tableA a
JOIN tableB b ON b.id = 1
WHERE a.id = 2),
sum3 AS (SELECT value * b AS res
FROM tableA a
JOIN tableB b ON b.id = 2
WHERE a.id = 3)
SELECT SUM(sum1.res + sum2.res + sum3.res)
FROM sum1, sum2, sum3;
I've tested #Hervé Piedvache's code and it returns NULL, because SELECT value * b AS val FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 1 has two rows. A work around would be:
SELECT SUM(val) FROM
(SELECT value * b AS val FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 1
UNION
SELECT value * c AS val FROM tableA a JOIN tableB b ON b.id = 1 WHERE a.id = 2
UNION
SELECT value * b AS val FROM tableA a JOIN tableB b ON b.id = 2 WHERE a.id = 3) data;

SQL Server, selecting from 2 columns from different tables

I have these columns from 2 tables
Table1 Table2
Code ID Code ID
A 1 A 1
B 1 B 1
C 1 C 1
D 1
E 1
My query:
Select
a.id, a.code, b.code
from
Table1 a, Table2 b
where
a.id = '1' and a.id = b.id
What I expected
ID code code
1 A A
1 B B
1 C C
1 D NULL
1 E NULL
What I got
ID code code
1 A A
1 B A
1 C A
1 D A
1 E A
1 A B
1 B B
1 C B
....
Any ideas? distinct didn't help
Thanks
Well, all the ID's in both tables are 1, so by joining on ID you'll get the cartesian product of both tables.
Instead, you'll need to do a left outer join based on Table1.Code:
Select a.id, a.code, b.code
from Table1 a LEFT OUTER JOIN Table2 b
on a.code = b.code
where a.id = '1';
You need to do a LEFT OUTER JOIN instead of a Cartesian Product
SELECT a.Id, a.Code, b.Code FROM Table1 a
LEFT OUTER JOIN Table2 b ON a.Code = b.Code
WHERE a.Id = '1'
A LEFT OUTER JOIN returns all rows from the left-hand side of the join (in this case Table 1) regardless of whether there is a matching record in the table on the right-hand side of the join (in this case Table 2). Where there is no match a NULL is returned for b.Code as per your requirements.
Reference OUTER JOINS

Postgresql query with left join and having

Postgresql 9.1: I have a query that must return the values of a second table only if the aggregate function SUM of two columns is greater than zero.
This is the data:
Table a
id
---
1
2
3
Table b
id fk(table a)
---------------
1 1
2 null
3 3
Table c
id fk(table b) amount price
-----------------------------------
1 1 1 10 --positive
2 1 1 -10 --negative
3 3 2 5
As you can see, table b has some ids from table a, and table c can have 1 or more references to table b, table c is candidate to be retrieved only if the sum(amount * price ) > 0.
I wrote this query:
SELECT
a.id, b.id, SUM(c.amount * c.price) amount
FROM
tablea a
LEFT JOIN
tableb b ON b.fk = a.id
LEFT JOIN
tablec c ON c.fk = b.id
GROUP BY
a.id, b.id
HAVING
SUM(c.amount * c.price) > 0
But this query is not retrieving all rows from table a just the row 1 and I need the two rows. I understand this is happening because of the HAVING clause but I don't know how to rewrite it.
Expected result
a b sum
------------------
1 null null -- the sum of 1 * 10 (rows 1 and two) = 0 so its not retrieved.
2 null null -- no foreign key in second table
3 3 10 -- the sum of 2 * 5 (row 3) > 0 so it's ok.
Try this:
SELECT A.ID, B.ID, C.ResultSum
FROM TableA A
LEFT JOIN TableB B ON (B.FK = A.ID)
LEFT JOIN (
SELECT FK, SUM(Amount * Price) AS ResultSum
FROM TableC
GROUP BY FK
) C ON (C.FK = B.ID) AND (ResultSum > 0)
See demo here.

How can I get the exact match join for the below scenario?

How can i join the below tables
TableA TableB TableC TableD
ID ID_C ID ID_A Value ID ID ID_C Value
1 1 1 1 a 1 1 1 a
2 1 b 2 1 b
in order to get the Result like
Result
ID ID_B Value ID_C ID_D Value
1 1 a 1 1 a
1 2 b 1 2 b
and my result shouldn't contain 1 2 b 1 1 b and both value columns cannot always have same values so it cannot be used in a condition.
To make it simplier,
Resultant Table TableA TableB
ID Value ID Value ID ID_A
1 a 1 a 1 1
1 b 2 g 2 1
2 a 3 d 3 2
3 c 4 3
Now i need to join the Resultant Table with TableA,TableB inorder to get some of the columns from TableA,TableB and ResultantTable.ID=TableA.ID and TableB.ID_A=TableA.ID since its a foreign key.
Doing the Join with TableB turns to duplicates. Since ID=1 occurs twice i get 4 records where ID=1, when there are only 2 records. It can be done with distinct or group by but i need other columns as well to be displayed.How do i do both in the process.
SELECT A.ID, B.ID, B.Value, C.ID, D.ID, D.Value
FROM TableA A
INNER JOIN TableB B ON A.ID = B.ID_A
INNER JOIN TableC C ON A.ID_C = C.ID
INNER JOIN TableD D ON B.ID = D.ID AND C.ID = D.ID_C
You tell us that the field "value" in TableB should not be different from the field "value" in TableD? Could we replace the B.ID = D.ID with B.Value = D.Value so solve your problem?
Are you sure, that is the way that is suppose to work?
Try:
SELECT A.ID, B.ID ID_B, B.Value Value_B, C.ID ID_C, D.ID ID_D, D.Value Value_D
FROM TableA A
JOIN TableB B ON A.ID = B.ID_A
JOIN TableC C ON A.ID_C = C.ID
JOIN TableD D ON B.Value = D.Value AND C.ID = D.ID_C