MySQL: different results with a WHERE statement included in master-detail query? - mysql

Let's say I have a mastertable (table1) with a detailtable (table2). There can be multiple detail records for each masterrecord. Now I want a query that counts all detailrecords for each masterrecord :
SELECT t1.id, count(t2.*)
FROM table1 as t1
LEFT JOIN table2 AS t2 ON t2.id=t1.id
GROUP BY t1.id
This gives me exactly the same number of records as table1 has.
But when I add a WHERE statement to only count the records that have a checkfield that's higher than 0, I don't get all records in table1 anymore! The ones with no matching detailrecords are now left out completely. Why is this happening?
SELECT t1.id, count(t2.*)
FROM table1 as t1
LEFT JOIN table2 AS t2 ON t2.id=t1.id
WHERE t2.checkfield != 0
GROUP BY t1.id
(Maybe something else is wrong in my real query, since I tried to simplify it for this example, but I think I got it right)

The WHERE clause restricts the joined results which are being aggregated over, so while you're trying do an outer join, only those rows with t2.checkfield != 0 survive, but that excludes all the unmatched rows!
On the other hand, when you change WHERE to AND, you now have tab1 LEFT OUTER JOIN tab2 ON(tab1.id = tab2.t1_id AND some_condition) -- but this is still an outer join, i.e. records on the left which have no match on the right will be included.

Related

MySQL LEFT JOIN a ON b compared to including WHERE a IN (b)

When I run the following query I get 2769 rows returned.
SELECT *
FROM table1 t1
LEFT JOIN table2 t2 ON t2.account_id = t1.account_id;
However, when I add the WHERE clause below, I get 692 Lines returned.
SELECT *
FROM table1 t1
LEFT JOIN table2 t2 ON t2.account_id = t1.account_id
WHERE t2.account_id IN (t1.account_id);
I thought that the condition established by my LEFT JOIN would the same condition established by my WHERE clause (i.e. that these two lines would effectively be redundant).
This is clearly not the case, but I cannot figure out why.
The left join returns all records of t1 and returns null for the columns of t2 for all records where the join could not be made.
But the where clause filters all data, no matter from which table. So when you filter on t2 in your where clause then all records where the join could not be made (and the t2.account_id is null) get excluded from the result since null != t1.account_id.
So basically your where clause turn your left join into an inner join.

SQL Count + Left join + Group by ... Missing rows

Trying to list all what's in table 1 and records under it in table 2
Table one each row has an id , and each row in table 2 has idontable1
select table1.*, count(table2.idintable1)as total
from table1
left join table2 on table1.id=table2.idintable1
WHERE table1.deleted='0' AND table2.deleted=0
group by
table2.idintable1
My current problem is rows from table1 with 0 records in table2 are not displayed
I want them to be displayed
The query that you want is:
select t1.*, count(t2.idintable1) as total
from table1 t1 left join
table2 t2
on t1.id = t1.idintable1 and t2.deleted = 0
where t1.deleted = 0
group by t1.id;
Here are the changes:
The condition on t2.deleted was moved to the on clause. Otherwise, this turns the outer join into an inner join.
The condition on t1.deleted remains in the where clause, because presumably you really do want this as a filter condition.
The group by clause is based on t1.id, because t2.idintable1 will be NULL when there are no matches. Just using t1.id is fine, assuming that id is unique (or a primary key) in table1.
The table aliases are not strictly necessary, but they make queries easier to write and to read.
You should GROUP BY table1.id.
The LEFT JOIN ensures all the rows from table1 appear in the result set. Those that do not have a pair in table2 will appear with NULL in field table2.idintable1. Because of that your original GROUP BY clause produces a single row for all the rows from table1 that do not appear in table2 (instead of one row for each row of table1).
You have fallen into mysql's non-standard group by support trap.
Change your group by to list all columns of table 1:
group by table1.id, table1.name, etc
or list the column positions of all table1 columns in the select:
group by 1, 2, 3, 4, etc
Or use a subquery to get the count vs the id, and join table1 to that.

Sql queries difference

I would like to know whether this two versions are equivalent in result and which is better for performance reasons and why?
Nested Select in Select version
select
t1.c1,
t1.c2,
(select Count(t2.c1) from t2 where t2.id = t1.id) as count_t
from
t1
VS
select t1.c1,t1.c2, Count(t2.c1)
from t1,t2
where t2.id= t1.id
The first query is analog of this query -
SELECT
t1.c1,
t1.c2,
COUNT(t2.c1)
FROM t1
LEFT JOIN t2
ON t2.id = t1.id;
It selects all records from first table, and all matched records from second table (it is LEFT JOIN condition).
The second is analog of this query -
SELECT
t1.c1,
t1.c2,
COUNT(t2.c1)
FROM t1
JOIN t2
ON t2.id = t1.id;
It selects only matched records in both tables (it is INNER JOIN condition).
Well they are different queries. The top one will select all rows from t1 returning 0 for the count if there is no matching id in table t2.
The second query will only return rows where t1 and t2 both have a row with the same id.
The first query will likely suffer from performance issues on large data sets. The second query will potentially have a Cartesian issue. I would go with a join or left join based on your intent to have records from table 1 if table 2 has no related records and then add a group by statement to control the Cartesian.

Can you count duplicate rows, left join to the appropriate row and then group all within 1 table?

I seriously don't know if its possible but Can you count duplicate rows, left join to the appropriate row and then group all within 1 table?
SELECT count(name)
FROM table1 t1, table2 t2
WHERE t1.name = t2.name;
This will give you the count of rows that the name in table1 is the same as in table2. I am assuming that is what you mean by duplicates. A left outer is not needed as it will show records from the left (table1) that do not have a match on the right (table2). If they do not have a match they are n

How to retrieve non-matching results in mysql

I'm sure this is straight-forward, but how do I write a query in mysql that joins two tables and then returns only those records from the first table that don't match. I want it to be something like:
Select tid from table1 inner join table2 on table2.tid = table1.tid where table1.tid != table2.tid;
but this doesn't seem to make alot of sense!
You can use a left outer join to accomplish this:
select
t1.tid
from
table1 t1
left outer join table2 t2 on
t1.tid = t2.tid
where
t2.tid is null
What this does is it takes your first table (table1), joins it with your second table (table2), and fills in null for the table2 columns in any row in table1 that doesn't match a row in table2. Then, it filters that out by selecting only the table1 rows where no match could be found.
Alternatively, you can also use not exists:
select
t1.tid
from
table1 t1
where
not exists (select 1 from table2 t2 where t2.tid = t1.tid)
This performs a left semi join, and will essentially do the same thing that the left outer join does. Depending on your indexes, one may be faster than the other, but both are viable options. MySQL has some good documentation on optimizing the joins, so you should check that out..