MYSQL: Where all values in joined table are the same - mysql

Lets say we have two tables A and B.
Table A
id name
1 first row
2 second row
Table B
id table_a_id voided
1 1 true
2 1 true
3 2 false
4 2 true
So, I want to select all rows from table A, only if all entries from table B marked as voided.
I started with simple query
SELECT * FROM table_a a
INNER JOIN table_b b ON a.id = b.table_a_id
WHERE b.voided = true
and now I'm stuck, the query returns both rows which is logically correct, how to rewrite this query, please?

You could use:
SELECT a.id, a.name
FROM a
JOIN b
ON a.id = b.table_a_id
GROUP BY a.id, a.name
HAVING SUM(CASE WHEN voided THEN 0 ELSE 1 END) = 0;
DBFiddle Demo
Even simpler(without CASE):
SELECT a.id, a.name
FROM a
JOIN b
ON a.id = b.table_a_id
GROUP BY a.id, a.name
HAVING SUM(NOT voided) = 0;
-- HAVING NOT SUM(NOT voided);

You could use a NOT EXISTS clause:
SELECT *
FROM table_a as a
where not exists (select *
from table_b as b
where a.id=b.id and not b.voided);

select *
from table_a
where id not in (select table_a_id
from table_b
where voided = false );

Related

How to left join when only matched one record from right table

I want to know How to left join when only matched one record from right table.
For example,
tableA
id
value
1
34
2
42
3
60
tableB
id
value
tableA_id
1
20
1
2
31
1
3
50
2
I want to get result like below using left outer join.
tableA_id
tableA_value
tableB_value
1
34
null
2
42
50
3
60
null
tableB_value of first row is null, because tableA.id = tableB.tableA_id matches multiple records.
how to solve it ?
Thanks.
You can make use of COUNT() as an analytic function to keep track of how many times a tableA_id occurs in the A table:
SELECT a.id AS tableA_id, a.value AS tableA_value, b.value AS tableB_value
FROM tableA a
LEFT JOIN
(
SELECT *, COUNT(*) OVER (PARTITION BY tableA_id) cnt
FROM tableB
) b
ON a.id = b.tableA_id AND b.cnt = 1
ORDER BY a.id;
Demo
You can do this using aggregation on the b table as well:
SELECT a.id AS tableA_id, a.value AS tableA_value, b.value AS tableB_value
FROM tableA a LEFT JOIN
(SELECT tableA_id, MAX(value) as value
FROM tableB
GROUP BY tableA_id
HAVING COUNT(*) = 1
) b
ON a.id = b.tableA_id
ORDER BY a.id;
This works because if there is only one row in B for a given id, then MAX() returns the value on that row.

MySQL Join - Retrieve Left Table Rows only if all the Right Table Rows satisfies the WHERE clause

I have two tables (Table A & Table B) in which a single parent row in 'Table A' will have multiple rows in 'Table B'. I have to retrieve rows from 'Table A', only if all the child rows in 'Table B' satisfies a WHERE clause.
Table A
id INT
name VARCHAR
gender VARCHAR
Table B
id INT
table_A_id INT
condition INT
Now I have to fetch the rows of 'Table A' for those which all the child rows in 'Table B' satisfies the WHERE clause 'condition=100'.
This query:
select table_A_id
from tableb
group by table_A_id
having sum(case when condition = 100 then 1 else 0 end) = count(*)
returns all the table_A_ids in tableb that meet the condition.
You can use it with IN like this:
select *
from tablea
where id in (
select table_A_id
from tableb
group by table_A_id
having sum(case when condition = 100 then 1 else 0 end) = count(*)
)
Or you can join the subquery:
select a.*
from tablea a inner join (
select table_A_id
from tableb
group by table_A_id
having sum(case when condition = 100 then 1 else 0 end) = count(*)
) b on b.table_A_id = a.id
Note that for MySql the HAVING clause can be simplified to this:
having sum(condition = 100) = count(*)
THis will do it:
select * from TableA A JOIN TableA B ON B.table_A_id = A.id
where not exists (select 1 from TableB B where B.table_A_id = A.id and condition <> 100)
I would simply recommend:
select a.*
from TableA a
where not exists (select 1
from TableB B
where B.table_A_id = A.id and
condition <> 100
);
If condition can be NULL, then you want:
where not exists (select 1
from TableB B
where B.table_A_id = A.id and
(condition <> 100 or condition is null)
);

SQL count rows in third table

A row in Table A can be linked to many rows in Table B. A row in Table B will be linked to either one or zero rows in Table C.
For a given row in Table A, I would like to count the (indirectly) linked rows in Table C.
I would like to return this for each row in Table A. The below doesn't give any errors, but doesn't give the correct value.
SELECT
*,
(
SELECT count(*)
FROM TABLE_A
INNER JOIN TABLE_B ON TABLE_A.id = TABLE_B.foreignKeyA
INNER JOIN TABLE_C ON TABLE_B.id = TABLE_C.foreignKeyB
) as cCount
FROM TABLE_A
Sample data:
TABLE_A
id
1
2
TABLE_B
id foreignKeyA
1 1
2 1
3 2
4 2
TABLE_C
id foreignKeyB
1 3
2 4
Should return (for the rows of Table A):
id cCount
1 0
2 2
I would suggest a correlated subquery:
SELECT a.*,
(SELECT count(*)
FROM TABLE_B b JOIN
TABLE_C c
ON c.foreignKeyB = b.id
WHERE b.foreignKeyA = a.id
) as aCount
FROM TABLE_A a;
Obviously, if you want the count for each row in TABLE_C, then you would adjust the table names and conditions.
You can do this with JOINs, but you need outer joins and an overall aggregation.
> with table_count as (
Select count(*) as cnt, TableB_FK
From TableB B
JOIN TableA A on A.FK = B.FK
Group By B.FK
)
select C.*, nvl(t.cnt,0)
from TableC C
left join table_count T on T.tableB_fk = C.FK
Table_count has aggregate count for foreign Keys.
Left Join table C with table count and replace null with 0
Change the INNER joins to LEFT joins for the case there are not any linked rows to TABLE_C, in which case you want 0 as result:
SELECT TABLE_A.id, count(TABLE_C.id) cCount
FROM TABLE_A
LEFT JOIN TABLE_B ON TABLE_B.foreignKeyA = TABLE_A.id
LEFT JOIN TABLE_C ON TABLE_C.foreignKeyB = TABLE_B.id
GROUP BY TABLE_A.id
See the demo.
Results:
| id | cCount |
| --- | ------ |
| 1 | 0 |
| 2 | 2 |

How can I return 1 row from a subquery in select statement if subquery has more than 1 result?

I have this query:
select a.*, b.*, (select c.* from tableC c where c.id_tableA = a.id) from tableA a inner join tableB b on a.id = b.id_tableA where b.id_user = 50;
The subquery (which is tableC) is returning me more than 1 row as expected. How can I return only 1 row from tableC so it could match with the rest of the query?
So far I have tried this:
(select c.* from tableC c where c.id_tableA = a.id limit 1)
It didn't work as mysql said:
"Operand should contain 1 column(s)"
You are mixing two things. Scalar subquery in SELECT list should return only one value (both row and column). Using LIMIT 1 will get you one row, but still many columns.
So you could specify column name:
select a.*, b.*,
(select c.col_name from tableC c where c.id_tableA = a.id order by .. limit 1)
from tableA a
inner join tableB b on a.id = b.id_tableA
where b.id_user = 50;
or use normal JOIN:
select a.*, b.*, c.*
from tableA a
inner join tableB b
on a.id = b.id_tableA
left join tableC c
on c.id_tableA = a.id
where b.id_user = 50;
if column id from table C is a primary key then it should have no problem
but if no, try to add another condition that will filter your subquery results like ,
for example here is the start_date:
SELECT a.column_1, b.column_2,
(SELECT column_3 FROM tableC
WHERE (id = a.id
AND (start_date = (SELECT MAX(b.start_date)
from tableC as c
where a.id = c.id ))) AS column_3
FROM tableA as a INNER JOIN
tableB as b ON b.id = a.id
WHERE b.id_user = 50;

query impossible for me?

I have 2 columns a and b with a 1:n relation:
A.id, a.text, a.b_id(fk), a.value --
B.id(pk), b.text etc
I want to create a query that returns the number of b.id with count(a.value=1) > 0
I tried this:
Select count(b.id)
from a.id
join b on a.b_id=b_id
group by b.id
having count(if(a.value=1),1,null))>0
...but without result. It seems simple but for me is a problem.
You don't need HAVING because standard INNER join won't return b rows without matching a rows anyway. You don't need GROUP BY either - use COUNT(DISTINCT ) instead:
SELECT COUNT(DISTINCT b.id) AS cnt
FROM b
JOIN a ON a.b_id = b.id
WHERE a.value = 1
This should do:
SELECT COUNT(*)
FROM TableB AS B
WHERE EXISTS(SELECT 1 FROM TableA
WHERE id = B.id
AND value = 1)
Try this:-
Select count(b.id) as x
FROM b
JOIN a ON a.b_id = b.id
WHERE a.value = 1
group by b.id
A simplification on #MarcinJuraszek's answer. If the foreign key can be trusted, there is no need to join table b:
SELECT COUNT(DISTINCT a.b_id) AS cnt
FROM a
WHERE a.value = 1 ;