I have two tables: post, post_image
I am currently trying to join them like so:
$sql = 'SELECT post.*, post_image.name AS img FROM post, post_image
WHERE post_image.postId=post.id LIMIT 10';
Here's my problem. Some posts do not have any entries in post_image, but I still need them returned as well. I know that mysql can check for null, but I'm not sure where it would go in my statement if that is the solution. I'm also not entirely sure I can do this with the shorthand join I am using.
Please help :)
Edit:
This is working as expected now, but I also need to make sure that it only pulls the post_image with field ordinal=0 as there can be multiple post_image entries. I tried adding a WHERE clause and it seemed to only pull posts with images.Here's what I have:
SELECT post.*, post_image.name AS img FROM post LEFT JOIN post_image ON post_image.postId=post.id WHERE post_image.ordinal=0 LIMIT 10
Use LEFT JOIN like this:
SELECT
post.*,
post_image.name AS img
FROM post
LEFT JOIN post_image ON post_image.postId = post.id
LIMIT 10;
Your query is INNER JOIN the two tables using the old join syntax which uses the WHERE clause to specify the join condition. Better off use the explicit JOIN syntax using the INNER JOIN condition instead in future queries.
For more information:
A Visual Explanation of SQL Joins.
Bad habits to kick : using old-style JOINs.
How about using LEFT JOIN? I think it will show the post although it does not have image.
SELECT * FROM post p LEFT JOIN post_image pi ON p.id = pi.postId
Related
I am finishing a SQL course and they have a query example that I don't quite understand.
I have
these tables
and I need to get how many 'etiquetas' (tags in Spanish), are in each post, so they have this solution:
SELECT posts.titulo, COUNT(*) num_etiquetas
FROM posts
INNER JOIN posts_etiquetas ON posts.id = posts_etiquetas.post_id
INNER JOIN etiquetas ON etiquetas.id = posts_etiquetas.etiqueta_id
GROUP BY posts.id
ORDER BY num_etiquetas DESC;
I have been trying to understand this query and two questions came up:
Why COUNT(asterisk) does the same as COUNT(etiquetas.nombre)? For me only the latter makes sense, I don't quite understand why COUNT(asterisk) works in the given solution, isn't COUNT(*) supposed to count the total number of rows? maybe the issue is that I don't really understand how GROUP BY really works.
Why does deleting this line doesn't change the result? What is its use in the original solution?
INNER JOIN etiquetas ON etiquetas.id = posts_etiquetas.etiqueta_id
This mysql statement gets posts that have not been flagged by the user.
As it is now, I am getting the flagged post ids, and then not getting posts in that set of ids.
SELECT * FROM posts WHERE posts.id NOT IN
(SELECT p2.id FROM posts p2 LEFT JOIN flagged_posts
ON flagged_posts.user_id = ? WHERE flagged_posts.post_id = p2.id)
I feel there is probably a better (faster) way to do this, for example with just one select and one join, but I am not sure
You can do this using clause EXISTS.
SELECT * FROM posts
WHERE
NOT EXISTS
(SELECT 1
FROM flagged_posts
WHERE flagged_posts.post_id=posts.post_id AND flagged_posts.user_id=?)
Alternatively you can do this using LEFT OUTER JOIN.
SELECT *
FROM posts
LEFT OUTER JOIN flagged_posts ON posts.post_id = flagged_posts.post_id
AND flagged_posts.user_id=?
WHERE flagged_posts.post_id IS NULL
I'm working on someone else's project. There is a query like this:
SELECT posts.id, posts.title, posts.body, posts.keywords
FROM posts
INNER JOIN pivot ON pivot.post_id = posts.id
INNER JOIN tags ON tags.id = pivot.tag_id
WHERE tags.name IN ( :keywords )
GROUP BY posts.id
The new policy is to replace IN with =. So the query I've written looks like this:
SELECT posts.id, posts.title, posts.body, posts.keywords
FROM posts
INNER JOIN pivot ON pivot.post_id = posts.id
INNER JOIN tags ON tags.id = pivot.tag_id
WHERE tags.name = :keyword
GROUP BY posts.id
Now I want to know, is GROUP BY redundant in this case? I say so because I think the reason of GROUP BY is omitting duplicate posts which are matched by each keyword.
First things first, when using GROUP BY within a SELECT statement every column that is not included within the grouping clause should be wrapped up with an aggregate function.
Just because MySQL allows this kind of odd behaviour doesn't make it best practices. Other DBMS for example PostgreSQL wouldn't allow this query to execute at all.
Saying that, how it works internally within MySQL is just that you get a unique record for each posts.id, but random values from potentially different rows for all non-aggregated and non-grouped column.
You should be using DISTINCT from what I can see.
Answer to your question
Replacing IN with = doesn't affect grouping at all, so you are free to go with it especially if you are not passing list but a single value to that query, but GROUP BY is not redundant in any case (or should be completely removed in both). It would change the output you receive.
If you, for instance, grouped by a unique column within a table and join that to a table with 1:1 relationship GROUP BY would be redundant. As a second example constructing proper WHERE clause with conditions might make it redundant as well.
I'm trying to write more complex/better mysql statements however I'm getting a bit tripped up on using logic to create the statement and am not completely sure how to go about it. Any help is appreciated!
Basically my goal statement is to Select all from the product table, and then for every product join an image where display = 1
"SELECT p
FROM CurbbedUserBundle:Product p
LEFT JOIN CurbbedUserBundle:ProductImages i
ON p.id = i.id
WHERE i.display = 1
ORDER BY p.name ASC"
Thank you for any help!
If you want a left join, then all the conditions on the second table should be in the on clause. You need to move the condition in the where to the on:
SELECT p
FROM CurbbedUserBundle:Product p LEFT JOIN
CurbbedUserBundle:ProductImages i
ON p.id = i.id AND i.display = 1
ORDER BY p.name ASC
EDIT:
The rule for a left join is that it keeps all rows in the first table regardless of whether the on conditions is true or not. Hence, if you want to filter by the first table, put the condition in the where clause. It won't do anything in the on. The rule for right join is exactly the opposite.
It is common to use SELECT within SELECT to reduce the number of queries; but as I examined this leads to slow query (which is obviously harmful for mysql performance). I had a simple query as
SELECT something
FROM posts
WHERE id IN (
SELECT tag_map.id
FROM tag_map
INNER JOIN tags
ON tags.tag_id=tag_map.tag_id
WHERE tag IN ('tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6')
)
This leads to slow queries of "query time 3-4s; lock time about 0.000090s; with about 200 rows examined".
If I split the SELECT queries, each of them will be quite fast; but this will increase the number of queries which is not good at high concurrency.
Is it the usual situation, or something is wrong with my coding?
In MySQL, doing a subquery like this is a "correlated query". This means that the results of the outer SELECT depend on the result of the inner SELECT. The outcome is that your inner query is executed once per row, which is very slow.
You should refactor this query; whether you join twice or use two queries is mostly irrelevant. Joining twice would give you:
SELECT something
FROM posts
INNER JOIN tag_map ON tag_map.id = posts.id
INNER JOIN tags ON tags.tag_id = tag_map.tag_id
WHERE tags.tag IN ('tag1', ...)
For more information, see the MySQL manual on converting subqueries to JOINs.
Tip: EXPLAIN SELECT will show you how the optimizer plans on handling your query. If you see DEPENDENT SUBQUERY you should refactor, these are mega-slow.
You could improve it by using the following:
SELECT something
FROM posts
INNER JOIN tag_map ON tag_map.id = posts.id
INNER JOIN tags
ON tags.tag_id=tag_map.tag_id
WHERE <tablename>.tag IN ('tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6')
Just make sure you only select what you need and do not use *; also state in which table you have the tag column so you can substitute <tablename>
Join does filtering of results. First join will keep results having 1st ON condition satisfied and then 2nd condition gives final result on 2nd ON condition.
SELECT something
FROM posts
INNER JOIN tag_map ON tag_map.id = posts.id
INNER JOIN tags ON tags.tag_id = tag_map.tag_id AND tags.tag IN ('tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6');
You can see these discussions on stack overflow :
question1
question2
Join helps to decrease time complexity and increases stability of server.
Information for converting sub queries to joins:
link1
link2
link3