MySQL inner join fails - mysql

I got this simple join statement and I'm pretty sure the syntax is correct. I looked some tutorials and I don't find any difference between my code and the exemples.
Here's the statement:
SELECT n.id nId, n.news_date, n.news_type,
p.id pId, p.title pTitle, p.file_path pPath,
s.id sId, s.title sTitle, s.content sContent,
v.id vId, v.title vTitle, v.url vUrl
FROM photo_news p, standard_news s, video_news v
INNER JOIN news n
ON p.news_id = n.id OR s.news_id = n.id OR v.news_id = n.id
ORDER BY n.news_date DESC
I get the following error:
Unknown column 's.news_id' in 'on clause'
I really don't know why this error is launched because the column 'news_id' exists in every table it has to exist.
And if I change the order in the ON clause (i.e. I start with p.news_id = n.news_id) I get the same error (unknwonw column p.news_id). So I think there's a problem with the aliases but I really don't have a clue.
Thanks for your help ;)

Probably you are looking for something like this to return data for the record in photo news with data in at least one of the other table.
In that case you need to use a LEFT JOINs and not OR in the JOIN conditions.
SELECT n.id nId, n.news_date, n.news_type,
p.id pId, p.title pTitle, p.file_path pPath,
s.id sId, s.title sTitle, s.content sContent,
v.id vId, v.title vTitle, v.url vUrl
FROM news n
LEFT OUTER JOIN photo_news p
ON n.id = p.news_id
LEFT OUTER JOIN standard_news s
ON n.id = s.news_id
LEFT OUTER JOIN video_news v
ON n.id = v.news_id
WHERE p.news_id IS NOT NULL
OR s.news_id IS NOT NULL
OR v.news_id IS NOT NULL
ORDER BY n.news_date DESC

Try this, You made mistake in JOINing tables.
for reference you can see how multiple tables are JOINed together.
SELECT n.id nId, n.news_date, n.news_type,
p.id pId, p.title pTitle, p.file_path pPath,
s.id sId, s.title sTitle, s.content sContent,
v.id vId, v.title vTitle, v.url vUrl
FROM photo_news p INNER JOIN standard_news s
p.news_id = s.news_id
INNER JOIN video_news v
on s.news_id = v.news_id
INNER JOIN news n
on v.news_id = n.id
ORDER BY n.news_date DESC

You are mixing only-style and new-style joins. Just use the explicit join syntax. Your from should probably be:
FROM news n join
photo_news p
on p.news_id = n.id join
standard_news s
on s.news_id = n.id join
video_news v
on v.news_id = n.id
Using or between join conditions is not typically used.
THe error is appearing because of the precedence rules that MySQL uses. As the documentation explains:
INNER JOIN and , (comma) are semantically equivalent in the absence of
a join condition: both produce a Cartesian product between the
specified tables (that is, each and every row in the first table is
joined to each and every row in the second table).
However, the precedence of the comma operator is less than of INNER
JOIN, CROSS JOIN, LEFT JOIN, and so on. If you mix comma joins with
the other join types when there is a join condition, an error of the
form Unknown column 'col_name' in 'on clause' may occur. Information
about dealing with this problem is given later in this section.
All that said, I'm not sure that this is really the query that you want. You are going to get a cartesian product of the different values from the different tables. You should probably ask another question with sample data and desired results, so someone can help you with the right query.

You are using deprecated join syntax mixed with supported syntax.
do yourself a favor and write those joins properly
http://dev.mysql.com/doc/refman/5.0/en/join.html
SELECT n.id nId, n.news_date, n.news_type,
p.id pId, p.title pTitle, p.file_path pPath,
s.id sId, s.title sTitle, s.content sContent,
v.id vId, v.title vTitle, v.url vUrl
FROM photo_news p
left/inner/right/"" join standard_news s on CONDITION
left/inner/right/"" join video_news v on CONDITION
INNER JOIN news n
ON p.news_id = n.id OR s.news_id = n.id OR v.news_id = n.id
ORDER BY n.news_date DESC
However, i am pretty sure you want to use union or something
SELECT ...
FROM
select * from (photo_news p
union all standard_news s
union all video_news v ) all_news
INNER JOIN news n on CONDITION

Related

Multiple aggregate functions in SQL query

For this example I got 3 simple tables (Page, Subs and Followers):
For each page I need to know how many subs and followers it has.
My result is supposed to look like this:
I tried using the COUNT function in combination with a GROUP BY like this:
SELECT p.ID, COUNT(s.UID) AS SubCount, COUNT(f.UID) AS FollowCount
FROM page p, subs s, followers f
WHERE p.ID = s.ID AND p.ID = f.ID AND s.ID = f.ID
GROUP BY p.ID
Obviously this statement returns a wrong result.
My other attempt was using two different SELECT statements and then combining the two subresults into one table.
SELECT p.ID, COUNT(s.UID) AS SubCount FROM page p, subs s WHERE p.ID = s.ID GROUP BY p.ID
and
SELECT p.ID, COUNT(f.UID) AS FollowCount FROM page p, follow f WHERE p.ID = f.ID GROUP BY p.ID
I feel like there has to be a simpler / shorter way of doing it but I'm too unexperienced to find it.
Never use commas in the FROM clause. Always use proper, explicit, standard JOIN syntax.
Next, learn what COUNT() does. It counts the number of non-NULL values. So, your expressions are going to return the same value -- because f.UID and s.UID are never NULL (due to the JOIN conditions).
The issue is that the different dimensions are multiplying the amounts. A simple fix is to use COUNT(DISTINCT):
SELECT p.ID, COUNT(DISTINCT s.UID) AS SubCount, COUNT(DISTINCT f.UID) AS FollowCount
FROM page p JOIN
subs s
ON p.ID = s.ID JOIN
followers f
ON s.ID = f.ID
GROUP BY p.ID;
The inner joins are equivalent to the original query. You probably want left joins so you can get counts of zero:
SELECT p.ID, COUNT(DISTINCT s.UID) AS SubCount, COUNT(DISTINCT f.UID) AS FollowCount
FROM page p LEFT JOIN
subs s
ON p.ID = s.ID LEFT JOIN
followers f
ON p.ID = f.ID
GROUP BY p.ID;
Scalar subquery should work in this case.
SELECT p.id,
(SELECT Count(s_uid)
FROM subs s1
WHERE s1.s_id = p.id) AS cnt_subs,
(SELECT Count(f_uid)
FROM followers f1
WHERE f1.f_id = p.id) AS cnt_fol
FROM page p
GROUP BY p.id;

Returning all results of an outer query and getting a count of attached items

So I'm struggling to write a query that returns me all categories regardless of what filter I have applied but the count changes based on how many returned recipes there will be in this filter.
This query works nice if I don't apply any filters to it. The count's seem right, but as soon as I add something like this: where c.parent_id is not null and r.time_cook_minutes > 60 I am filtering out most of the categories instead of just getting a count of zero.
here's an example query that I came up with that does not work the way I want it to:
select t.id, t.name, t.parent_id, a.cntr from categories as t,
(select c.id, count(*) as cntr from categories as c
inner join recipe_categories as rc on rc.category_id = c.id
inner join recipes as r on r.id = rc.recipe_id
where c.parent_id is not null and r.time_cook_minutes > 60
group by c.id) as a
where a.id = t.id
group by t.id
so this currently, as you might imagine, returns only the counts of recipes that exist in this filter subset... what I'd like is to get all of them regardless of the filter with a count of 0 if they don't have any recipes under that filter.
any help with this would be greatly appreciated. If this question is not super clear let me know, and I can elaborate.
No need for nested join if you move the condition into a regular outer join:
select t.id, t.name, t.parent_id, count(r.id)
from categories as t
left join recipe_categories as rc on rc.category_id = c.id
left join recipes as r on r.id = rc.recipe_id
and r.time_cook_minutes > 60
where c.parent_id is not null
group by 1, 2, 3
Notes:
Use left joins so you always get every category
Put r.time_cook_minutes > 60 on the left join condition. Leaving it on the where clause cancels the effect of left
Simply use conditional aggregation, moving the WHERE clause into a CASE (or IF() for MySQL) statement wrapped in a SUM() of 1's and 0's (i.e., counts). Also, be sure to consistently use the explicit join, the current industry practice in SQL. While your derived table uses this form of join, the outer query uses implicit join matching IDs in WHERE clause.
select t.id, t.name, t.parent_id, a.cntr
from categories as t
inner join
(select c.id, sum(case when c.parent_id is not null and r.time_cook_minutes > 60
then 1
else 0
end) as cntr
from categories as c
inner join recipe_categories as rc on rc.category_id = c.id
inner join recipes as r on r.id = rc.recipe_id
group by c.id) as a
on a.id = t.id
group by t.id
I believe you want:
select c.id, c.name, c.parent_id, count(r.id)
from categories c left join
recipe_categories rc
on rc.category_id = c.id left join
recipes r
on r.id = rc.recipe_id and r.time_cook_minutes > 60
where c.parent_id is not null and
group by c.id, c.name, c.parent_id;
Notes:
This uses left joins for all the joins.
It aggregates by all the non-aggregated columns.
It counts matching recipes rather than all rows.
The condition on recipes is moved to the on clause from the where clause.

Count matched words from IN operator

i have this little mysql query :
select t.title FROM title t
inner join movie_keyword mk on mk.movie_id = t.id
inner join keyword k on k.id = mk.keyword_id
where k.keyword IN (
select k.keyword
FROM title t
inner join movie_keyword mk on mk.movie_id = t.id
inner join keyword k on k.id = mk.keyword_id
where t.id = 166282
)
LIMIT 15
as you can see it will return all titles from title that have at least one the same keyword that have movie with id 166282.
Now i have problem, because i want also count how many keywords was matched in IN operator(let's say i want to see only titles that have 3 or more the same keywords), i tried something with aggregate functions, but everything failed, so i came here with my problem. Maybe somebody can give me some advice, or code example.
I'm not also sure, if this "subquery way" is good, so if there are some better options how i should solve my problem, I am open to any suggestions or tips.
Thank you!
#Edit
So after some problems, i have one more. This is my current query :
SELECT s.title,s.vote,s.rating,count(dk.key) as keywordCnt, count(dg.name) as genreCnt
FROM series s
INNER JOIN series_has_genre shg ON shg.series_id = s.id
INNER JOIN dict_genre dg ON dg.id = shg.dict_genre_id
INNER JOIN series_has_keyword shk ON shk.series_id = s.id
INNER JOIN dict_keyword dk ON dk.id = shk.dict_keyword_id
WHERE dk.key IN (
SELECT dki.key FROM series si
INNER JOIN series_has_keyword shki ON shki.series_id = si.id
INNER JOIN dict_keyword dki ON dki.id = shki.dict_keyword_id
WHERE si.title LIKE 'The Wire'
)
and dg.name IN (
SELECT dgo.name FROM series so
INNER JOIN series_has_genre shgo ON shgo.series_id = so.id
INNER JOIN dict_genre dgo ON dgo.id = shgo.dict_genre_id
WHERE so.title LIKE 'The Wire'
)
and s.production_year > 2000
GROUP BY s.title
ORDER BY s.vote DESC, keywordCnt DESC ,s.rating DESC, genreCnt DESC
LIMIT 5
Problem is, it is very, very, very slow. Any tips what i should change, to run it faster ?
Will this work for you:
select t.title, count(k.keyword) as keywordCount FROM title t
inner join movie_keyword mk on mk.movie_id = t.id
inner join keyword k on k.id = mk.keyword_id
where k.keyword IN (
select ki.keyword
FROM title ti
inner join movie_keyword mki on mki.movie_id = ti.id
inner join keyword ki on ki.id = mki.keyword_id
where ti.id = 166282
) group by t.title
LIMIT 15
Note that I have changed the table names inside the nested query to avoid confusion.

SQL - Find the object with the most appearances

I am a newbie to SQL working on an assignment to find the actor or actress with the most appearances. A diagram of the database I'm working with is here:
Here was the query I was trying to use:
SELECT DISTINCT n.name, count(n.name)
FROM cast_info c
INNER JOIN name n
ON (n.id = c.person_id)
INNER JOIN title t
ON (c.movie_id = t.id)
CROSS JOIN role_type r
WHERE (r.role = 'actor' OR r.role = 'actress')
GROUP BY n.name
This is intended to get a count of how many times different actors showed up, which I can then sort and select the top one. But it doesn't work. Something else I did was:
SELECT n.name, count(n.name) AS amount
FROM cast_info c
INNER JOIN name n
ON (n.id = c.person_id)
INNER JOIN title t
ON (c.movie_id = t.id)
LEFT JOIN role_type r
ON c.role_id = r.id
AND (r.role = 'actor' OR r.role = 'actress')
GROUP BY amount
ORDER BY amount DESC
LIMIT 1
But that gives the error
aggregate functions are not allowed in GROUP BY
LINE 1: SELECT COUNT(*) AS total FROM (SELECT n.name, count(n.name) ...
Tips?
I am going to take a stab at each of these questions for you, because this assignment is obviously causing you some trouble.
You can find everything you need in your cast_info table and your role_type table, unless you need to display the actors/actresses actual name.
I would start by selecting all rows that represent an actor or actress in a movie. This should be a unique combination, as a person can't be an actor in the same movie twice. Once you've done that, group by the persons id and get the count() of rows, which should effectively be the number of movies. I think the error you're getting is exactly for the reason it sounds, you can't use an aggregate column in your order by. A workaround for that would be to use this as a subquery, and use MAX() to get most appearances.
Try this:
SELECT c.personid, MAX(numMovies) AS mostApperances
FROM(SELECT c.personid, COUNT(*) AS numMovies
FROM cast_info c
JOIN role_type r ON r.id = c.role_id
WHERE r.role = 'actor' OR r.role = 'actress'
GROUP BY c.personid) t
Try this
SELECT DISTINCT n.name, count(n.name)
FROM cast_info c
INNER JOIN name n
ON n.id = c.person_id
INNER JOIN title t
ON c.movie_id = t.id
LEFT JOIN role_type r
ON c.role_id = r.id
AND (r.role = 'actor' OR r.role = 'actress')
GROUP BY n.name

How to use aliases with MySQL LEFT JOIN

My original query is doing joins using the WHERE clause rather than JOIN. I realized that this was not returning movies that did not have any stars or genres did not show up so I think I have to do a LEFT JOIN in order to show every movie. Here is my original SQL:
SELECT *
FROM movies m, stars s, stars_in_movies sm, genres g, genres_in_movies gm
WHERE m.id = sm.movie_id
AND sm.star_id = s.id
AND gm.genre_id = g.id
AND gm.movie_id = m.id
AND m.title LIKE '%the%'
AND s.first_name LIKE '%Ben%'
ORDER BY m.title ASC
LIMIT 5;
I tried to do a LEFT JOIN on movies I'm definitely doing something wrong.
SELECT *
FROM movies m, stars s, stars_in_movies sm, genres g, genres_in_movies gm
LEFT JOIN movies m1 ON m1.id = sm.movie_id
LEFT JOIN movies m2 ON m2.id = gm.movie_id
AND sm.star_id = s.id
AND gm.genre_id = g.id
ORDER BY m.title ASC
LIMIT 5;
I get ERROR 1054 (42S22): Unknown column 'sm.movie_id' in 'on clause' so clearly I'm doing the join wrong, I just don't see what it is.
Don't mix the comma operator with JOIN - they have different precedence! There is even a warning about this in the manual:
However, the precedence of the comma operator is less than of INNER JOIN, CROSS JOIN, LEFT JOIN, and so on. If you mix comma joins with the other join types when there is a join condition, an error of the form Unknown column 'col_name' in 'on clause' may occur. Information about dealing with this problem is given later in this section.
Try this instead:
SELECT *
FROM movies m
LEFT JOIN (
stars s
JOIN stars_in_movies sm
ON sm.star_id = s.id
) ON m.id = sm.movie_id AND s.first_name LIKE '%Ben%'
LEFT JOIN (
genres g
JOIN genres_in_movies gm
ON gm.genre_id = g.id
) ON gm.movie_id = m.id
WHERE m.title LIKE '%the%'
ORDER BY m.title ASC
LIMIT 5;
You should put your conditions related to your JOINs in the same ON clause. However, for your above problem, you should use the following query:
SELECT *
FROM movies m
LEFT JOIN stars_in_movies sm ON sm.movie_id = m.id
JOIN stars s ON sm.star_id = s.id
LEFT JOIN genres_in_movies gm ON gm.movie_id = m.id
JOIN genres g ON gm.genre_id = g.id
ORDER BY m.title ASC
LIMIT 5;
Maybe ugly, But the way it will work is here. Beware this is ugly and lot of people is giving warning about this kind of hacks
SELECT *
FROM movies m, stars_in_movies sm LEFT JOIN movies m1 ON m1.id = sm.movie_id, stars s
ORDER BY m.title ASC
LIMIT 5;
when using joins, you must do the join with the right table which have the columns you are comparing.
SQL Join (inner join in MySQL)
select emp1.id,emp1.name,emp1.job from (select id, type as name, description as job from component_type as emp1)emp1
inner join
emp
on emp1.id=emp.id;
Left Join
select emp1.id,emp1.name,emp1.job from (select id, type as name, description as job from component_type as emp1 where id between '1' AND '5')emp1
left join
emp
on emp1.id=emp.id;
Right Join
select emp1.id,emp1.name,emp1.job from (select id, type as name, description as job from component_type as emp1)emp1
Right join
(select * from emp where id between '1' and '5')exe
on emp1.id=exe.id;
Using alias connect many table without using join..
select sum(s.salary_amount) as total_expenses_paid_to_all_department
from salary_mas_tbl s,dept_mas_tbl d
where s.salary_dept=d.dept_id;