Two SQL Joins, Two Different Results - mysql

Hows is it possible for these two queries to be different. I mean the first query didn't include all the rows from my left table so I put the conditions within the join part.
Query 1
SELECT COUNT(*) as opens, hours.hour as point
FROM hours
LEFT OUTER JOIN tracking ON hours.hour = HOUR(FROM_UNIXTIME(tracking.open_date))
WHERE tracking.campaign_id = 83
AND tracking.open_date < 1299538799
AND tracking.open_date > 1299452401
GROUP BY hours.hour
Query 2
SELECT COUNT(*) as opens, hours.hour as point
FROM hours
LEFT JOIN tracking ON hours.hour = HOUR(FROM_UNIXTIME(tracking.open_date))
AND tracking.campaign_id = 83
AND tracking.open_date < 1299538799
AND tracking.open_date > 1299452401
GROUP BY hours.hour
The difference is that the first query gives me 18 rows where there are no rows between point 17 to 22. But when I run the second query, it shows the fully 24 rows but for rows between 17 and 22 it has a value of 1! I would of expected it to be 0 or NULL? If it really is 1 should it not have appeared in the first query?
How has this happened?

the first JOIN is really an INNER JOIN, the outer joined table should not appear in the WHERE clause like you have in the top query, instead of COUNT(*), pick a column from the outer joined table

You're using COUNT(*), which will count every row in your result set (as it's written), since even without data in tracking, you do have data in hours.
Try changing COUNT(*) to COUNT(tracking.open_date) (or any non-nullable column within tracking; it doesn't matter which one).

COUNT(*) counts the number of rows resulted in the query.
You can use count(tracking.open_date), basically any column from tracking table (right table)

The problem is that the first query will do an outer join, with some rows containing NULL in all tables from the tracking table. Then it will apply a filter on those tracking columns, and since they are null, the corresponding row from the result set will be filtered out.
The second query will do a proper outer join on all columns.

Related

left join with group by returns no rows if table does not contains the specific id

I want to find out how many people rated a specific location. the problem is that my sql ONLY works if the hasVotedLocation table CONTAINS THE ID OF THE SPECIFIC LOCATION. Meaning if nobody voted the specific location i get zero rows.
My sql statement:
select count(*),l.idLoc from Location l
left join hasVotedLocation hvl
on hvl.idLoc=l.idLoc where l.idLoc=2
group by l.idLoc
For idLoc=1 the query should return one row with count(*)=3 and idLoc=1
For idLoc=2 the query should return one row with count(*)=0 and idLoc=2
For idLoc=3 the query should return one row with count(*)=1 and idLoc=3
You can remove the GROUP BY:
select <someid> as idLoc, count(x)
from Location l left join
hasVotedLocation hvl
on hvl.idLoc = l.idLoc ;
This will always return one row. The count() will be 0 if there are no matching rows.
In a GROUP BY query COUNT(*) returns the number of rows in the group. However when the condition in the ON clause of the LEFT JOIN doesn't match any row in the right table, the result will be one row with all columns from the right table filled with NULLs. That is still one row - not zero rows. Thus COUNT(*) will never return a value less than 1 in this query. But the result will also not be "zero rows" (as you stated in your question), as long as the condition in the WHERE clause matches any row in the left table.
To get the number of matches in the right table (the number of votes in your case), you should count the number of non NULL values from the right table. It is usually best to use the same column as in the ON clause (this way the engine can use the same index without a second lookup), which would be COUNT(hvl.idLoc).
select count(hvl.idLoc), l.idLoc from Location l
left join hasVotedLocation hvl
on hvl.idLoc=l.idLoc where l.idLoc = ?
group by l.idLoc
However - If all you need is the number of votes for a specific idLoc value, you can just use a simple COUNT(*) query.
select count(*)
from hasVotedLocation
where idLoc = ?
Note: In this query COUNT(*) will return 0, if the WHERE condition doesn't match any row. This is because SELECT * would return zero rows.

Strange MySQL problems with LIMIT and JOIN

I have this query:
SELECT s.*
FROM #mcmodlist_servers s
LEFT OUTER JOIN #mcmodlist_tag_server ts
ON ts.server_id = s.id
(don't mind the #mcmodlist_ bits, it's converted by PHP into the actual table names).
When executed as written as above it gives a result of 5 records, as it should, but when I add LIMIT 10 it suddenly returns 4.
But wait, it gets even better: If I change it to LIMIT 12 there's suddenly 5 records again (LIMIT 11 still returns 4).
Left outer should join only if it has a matching record and otherwise return null, right?
Why is LIMIT behaving like this? it works just fine without the JOIN clause
I think if you run the query in a MySQL client, with limit 10, you will find that it is in fact returning 10 rows in the resultset.
I suspect that there are multiple rows in #mcmodlist_tag_server with a server_id that matches a row from #mcmodlist_servers. When there are multiple matching rows, you are going to get "duplicate" rows from #mcmodlist_servers.
Given that there are no columns returned from the #mcmodlist_tag_server table, and that this is an OUTER join, it's not at all clear why this table would be included in the query at all.
And no, LEFT JOIN does not mean what you said it means.
Q: Left outer should join only if it has a matching record and otherwise return null, right?
A: No. That's not what LEFT JOIN means. A LEFT JOIN will return all rows from the table on the left side, along with matching rows from the right side, just like an INNER JOIN. But with the LEFT JOIN, if there's a row from the left side that doesn't have a matching row from the right side, the row from the left is returned. Yes, when that happens, the columns from the rightside table will consist of NULL placeholders.
The LIMIT clause applies to the total number of rows returned in the resultset. It does not mean the number of distinct rows from a given table.

Mysql join query with where condition and distinct records

I have two tables called tc_revenue and tc_rates.
tc_revenue contains :- code, revenue, startDate, endDate
tc_rate contains :- code, tier, payout, startDate, endDate
Now I need to get records where code = 100 and records should be unique..
I have used this query
SELECT *
FROM task_code_rates
LEFT JOIN task_code_revenue ON task_code_revenue.code = task_code_rates.code
WHERE task_code_rates.code = 105;
But I am getting repeated records help me to find the correct solution.
eg:
in this example every record is repeated 2 time
Thanks
Use a group by for whatever field you need unique. For example, if you want one row per code, then:
SELECT * FROM task_code_rates LEFT JOIN task_code_revenue ON task_code_revenue.code = task_code_rates.code
where task_code_rates.code = 105
group by task_code_revenue.code, task_code_revenue.tier
If code admits duplicates in both tables and you perform join only using code, then you will get the cartessian product between all matching rows from one table and all matching rows from the other.
If you have 5 records with code 100 in first table and 2 records with code 100 in second table, you'll get 5 times 2 results, all combinations between matching rows from the left and the right.
Unless you have duplicates inside one (or both) tables, all 10 results will differ in colums coming either from one table, the other or both.
But if you were expecting to get two combined rows and three rows from first table with nulls for second table columns, this will not happen.
This is how joins work, and anyway, how should the database decide which rows to combine if it didn't generate all combinations and let you decide in where clause?
Maybe you need to add more criteria to the ON clause, such as also matching dates?

Access 2010 query still showing results when record empty

I am trying to make a query joining 4 tables. One table will only fill out some records, not all. How do I get the criteria to be:
If record is null still show the field..... because right now if the record is null in any one of the fields it will not show in my query results.
If records are missing in one table, then you must use an outer join. Say, you have a table A with 5 records and a table B with only 3 records and you want to make a query joining the two tables showing 5 records
SELECT * FROM
A
LEFT JOIN B
ON A.ID = B.FK
In the query designer, right click on the join-line and enter this:
It sounds like you want your filter conditions to ignore NULLs. (That is, by default a NULL wouldn't match your filter, but the desired output is that it should.)
If this is correct, you want to modify the filters to allow NULLs, for example by adding OR IS NULL.
It sounds like you need to use a LEFT (or "outer") Join instead of an INNER join.
Outer joins (like LEFT JOINs) will return all results whether there are matching records in the other table or not. You'll just have null column values in the results for the joined tables.
More info here: http://pcmcourseware.com/blog/2010/11/10/modifying-query-joins-in-microsoft-access/

Left Outer Join doesn't return all rows from my left table?

I am trying to get the number of page opens on a per day basis using the following query.
SELECT day.days, COUNT(*) as opens
FROM day
LEFT OUTER JOIN tracking ON day.days = DAY(FROM_UNIXTIME(open_date))
WHERE tracking.open_id = 10
GROUP BY day.days
The output I get it is this:
days opens
1 9
9 2
The thing is, in my day table, I have a single column that contains the number 1 to 30 to represent the days in a month. I did a left outer join and I am expecting to have all days show on the days column!
But my query is doing that, why might that be?
Nanne's answer given explains why you don't get the desired result (your WHERE clause removes rows), but not how to fix it.
The solution is to change WHERE to AND so that the condition is part of the join condition, not a filter applied after the join:
SELECT day.days, COUNT(*) as opens
FROM day
LEFT OUTER JOIN tracking
ON day.days = DAY(FROM_UNIXTIME(open_date))
AND tracking.open_id = 10
GROUP BY day.days
Now all rows in the left table will be present in the result.
You specify that the connected tracking.open_id must be 10. For the other rows it will be NULL, so they'll not show up!
The condition is in the WHERE clause. After joining the tables the WHERE conditions are evaluated to filter out everything matching the criteria.Thus anything not matching tracking.open_id = 10 gets discarded.
If you want to apply this condition while joining the two tables, a better way is to use it with the ON clause (i.e. joining condition) than the entire dataset condition.