sql query join where none of the many meet a condition - sql

I have 3 tables. On the first table, there are multiple entries for each project. The second is basically a mapping table. It's more complicated than this, but for this example I've simplified. There's a simple condition I'm checking for on table 2. On the third table, each entry has a flag that's set to true or false. I want to return rows on the first table where all matching rows on the third table are false. In the example below, the result would return project A b/c all of Jane and Fred's rows in table 3 are false, but none of the other's since every other project has at least one true entry in table 3.
Project | Client name | id id | active
--------------- ---------------- ---------------
A | Jane John | 1 1 | false
A | Fred Jane | 2 1 | true
B | Mary Fred | 3 2 | false
B | Jane Mary | 4 2 | false
C | John 3 | false
C | Jane 3 | false
D | Jane 4 | true
D | Mary 4 | false
D | John
D | Fred

The following should do what you want:
select t1.*
from table1 t1
where not exists (select 1
from table2 t2 join
table3 t3
on t2.id = t3.id
where t2.name = t1.name and t3.active <> false
);
There is some ambiguity about what to do when one of the joins fails (this condition is not present in the sample data). This will return the row, because all matching rows in the third table are false, even in that case.

SELECT t1.* FROM t1
INNER JOIN t2 ON t1.Client = t2.name
WHERE t2.id IN (
SELECT id FROM t3
GROUP BY id, active
HAVING SUM(CASE active WHEN false THEN 1 ELSE 0 END) = COUNT(1)
)

A fairly straight forward JOIN with HAVING should give you the results you want;
SELECT t1.project, t1.client
FROM table1 t1
JOIN table2 t2 ON t1.client = t2.name
JOIN table3 t3 ON t2.id = t3.id
GROUP BY t1.project, t1.client
HAVING NOT MAX(t3.active)
An SQLfiddle to test with.
This basically just does a straight forward join of all tables, groups the results by client and project. It then uses NOT MAX(t3.active) to check that all booleans in the group are false.
This version chooses to not return clients/projects that don't have any active flags to check.

You have to do the select from the first table with two joins and a simple WHERE condition:
SELECT
res.Project
FROM
(SELECT
table1.Project,
BOOL_OR(res) as active
FROM
table1
JOIN table2 ON table2.name=table1.Client
JOIN table3 ON table3.id=table2.id
GROUP BY table1.Project
) as res
WHERE
res.active=FALSE

Related

Sql want to get matching plus new records from tables

I am new to SQL queries, I have tables:
Table1:
Id | Flag
----+------
200 | 1
201 | 1
202 | 1
203 | 1
204 | 1
Table2:
Id | Flag
----+------
200 | 0
203 | 1
I want result like this:
Id | Flag
----+------
200 | 0
201 | 1
202 | 1
204 | 1
I have tried with left join but still I am not getting expected result.
It seems that you want a join where the Flag value from TableB is given priority over the value from TableA and where the Flag values do not match in both tables.
If that is the case, you could accomplish this with the use of a COALESCE() as well as a WHERE condition to remove items where the join fails and the matching Flag values):
SELECT a.ID,
COALESCE(b.Flag, a.Flag) Flag
FROM TableA a
LEFT JOIN TableB b
ON a.ID = b.ID
AND (b.Flag IS NULL OR a.Flag <> b.Flag)
Example
You can see an interactive working example here, which given your data outputs the expected values:
You seem to want all rows except for the ones with "1" in both flag columns. Then, if available you want the flag in table2.
If this is a correct interpretation:
select t1.id, coalesce(t2.flag, t1.flag)
from table1 t1 left join
table2 t2
on t1.id = t2.id
where t1.flag <> 1 or
(t2.flag is null or t2.flag <> 1)
Here is a db<>fiddle.

Take data from two tables and show in one row without duplicates with a where condition

I want to take the data from two tables and output them in one row .
output will have two columns "to" and "from" where the condition is "from" will be having data from second table where type is true and "to" column will have data from second table where type is false . FK_ID in second table is linked to ID on the first table . Please help with the query.
I was trying to do with inner joins and union was not able to make it work . Thanks in advance .
TABLE 1
ID | PATH|
1 | ABC |
2 | EFG |
TABLE 2
ID | FK_ID | NUMBER | TYPE
20 | 1 | 123 | TRUE
21 | 1 | 456 | FALSE
28 | 2 | 888 | FALSE
29 | 2 | 939 | TRUE
OUTPUT SHOULD BE:
ID | PATH | TO | FROM
1 | ABC | 456 | 123
2 | EFG | 888 | 939
Use aggregation with pivoting logic to identify the "to" and "from" components of each path:
SELECT
t1.ID,
t1.PATH,
MAX(CASE WHEN t2.TYPE = 'FALSE' THEN t2.NUMBER END) AS "TO",
MAX(CASE WHEN t2.TYPE = 'TRUE' THEN t2.NUMBER END) AS "FROM"
FROM table1 t1
LEFT JOIN table2 t2
ON t1.ID = t2.FK_ID
GROUP BY
t1.ID,
t1.PATH
ORDER BY
t1.ID;
If performance is an issue, you might find a lateral join to be faster:
SELECT t1.*, t2.*
FROM table1 t1 LEFT JOIN LATERAL
(SELECT SUM(T2.NUMBER) FILTER (WHERE NOT t2.TYPE) as num_to,
SUM(T2.NUMBER) FILTER (WHERE t2.TYPE) as num_from
FROM table2 t2
WHERE t1.ID = t2.FK_ID
) t2
ORDER BY t1.ID;
This avoids the outer GROUP BY and probably the sorting as well (assuming that ID is the primary key).
It also assumes that TYPE is a Postgres boolean type. If not, use string comparisons for the WHERE clauses.

How to do an outer join with full result between two tables

I have two tables:
TABLE1
id_attr
-------
1
2
3
TABLE2
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
As a result I want a table that show:
RESULT
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
So I want the row with id=10 and id_attr=3 also when id_Attr=3 is missing in TABLE2 (and I know that because I have a NULL value (or something else) in the val column of RESULT.
NB: I could have others ids in table2. For example, after insert this row on table2: {11,1,A}, as RESULT I want:
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
11 | 1 | A
11 | 2 | NULL
11 | 3 | NULL
So, for every id, I want always the match with all id_attr.
Your specific example only has one id, so you can use the following:
select t2.id, t2.id_attr, t2.val
from table2 t2
union all
select 10, t1.id_attr, NULL
from table1 t1
where not exists (select 1 from table2 t2 where t2.id_attr = t1.id_attr);
EDIT:
You can get all combinations of attributes and ids in the following way. Use a cross join to create all the rows you want and then a left join to bring in the data you want:
select i.id, t1.id_attr, t2.val
from (select distinct id from table2) i cross join
table1 t1 left join
table2 t2
on t2.id = i.id and t2.id_attr = t1.id_attr;
It sounds like you want to do just an outer join on id_attr instead of id.
select * from table2 t2
left outer join table1 t1 on t2.id_attr = t1.id_attr;

MS SQL Where one column is x or y and both returned

I have a table as follows with dates in. The table has many more records but simplified for asking purposes:
Name | Date | Grade
Person 1 | 01-01-2001 | B
Person 1 | 31-01-2001 | A
Person 2 | 01-01-2001 | C
Person 3 | 31-01-2001 | A
I want to return both records for Person 1 but not either of the other two. AND returns nothing obviously and OR returns everything. I want to search on the date not the grade or the person.
So the result would be:
Name | Date | Grade
Person 1 | 01-01-2001 | B
Person 1 | 31-01-2001 | A
One simple way to handle this is to aggregate by person and then assert that the two dates of interest are both present:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT Name
FROM yourTable
WHERE Date IN ('2001-01-01', '2001-01-31')
GROUP BY Name
HAVING COUNT(DISTINCT Date) = 2
) t2
ON t1.Name = t2.Name
You can uses EXISTS to return a row if there exists another row with that name, having the other A/B grade.
select t1.*
from tablename t1
where t1.Date in ('2001-01-01', '2001-01-31')
and exists (select 1 from tablename t2
where t2.Name = t1.Name
and t2.Date in ('2001-01-01', '2001-01-31')
and t2.Date <> t1.Date)

Access VBA: Select only multiple values

Say, I have a table that looks like this:
ID | PNo | MM | CP |
---|-----|------|----|
1 | 13 | True | 4 |
2 | 92 | True | 3 |
3 | 1 | True | 3 |
4 | 13 | False| 2 |
5 | 13 | True | 3 |
6 | 1 | True | 3 |
I want to go through all PNos and compare all rows with that PNo and only select those that have different values in field MM.
My plan was to create a table with the distinct values of PNo, iterate through that table by using the usual record set and write an SQL query for each PNo.
Now my problem is the construction of the SQL query.
I can select all rows with Table.PNo = rs("PNo") but I have no idea how to formulate the query to catch the rows with varying values.
You can use a subquery:
Select *
From YourTable
Where PNo IN
(Select T.PNo
From YourTable
Group By PNo, MM
Having Count(*) = 2)
I think this should work.
This will create a cartesian product on y our PNo field. i.e. Every record connected to every record (but just on that PNo).
SELECT *
FROM Table1 T1 INNER JOIN Table1 T2 ON T1.PNo = T2.PNo
You'll end up with 9 instances of PNo 13, 4 of 1 and 1 of 92. Now we just want to return the ones where MM is different, so add that to the WHERE clause.
SELECT *
FROM Table1 T1 INNER JOIN Table1 T2 ON T1.PNo = T2.PNo
WHERE T1.MM <> T2.MM
ORDER BY T1.ID
This will return four records. PNo 1 and 92 will have vanished as the MM result was the same for those. ID number 4 will be returned twice as the MM value is different from that in ID 1 and ID 5.
To remove the duplicate value you could then use DISTINCT:
SELECT DISTINCT T1.ID, T1.PNo, T1.MM, T1.CP
FROM Table1 T1 INNER JOIN Table1 T2 ON T1.PNo = T2.PNo
WHERE T1.MM <> T2.MM
ORDER BY T1.ID
Note: One of the differences between the answer given by #Jonathan and mine is that his query is updateable and mine isn't.
The following should do what you want:
SELECT * FROM MyTable WHERE PNo in
(SELECT t.PNo FROM MyTable t
INNER join MyTable f
ON t.PNo = f.PNo
WHERE t.MM = true and f.MM = false)
The inner join ensures that only those PNos that have both MM false and MM true are included.