Mysql selecting values from 2 tabes - mysql

I have a table called "articles" which has a column called article char and photo_ID int.
And I also have a table called photos.
It has 2 columns photo_ID and photo_name.
I need to select photo name from table photos, but take its ID from table articles.
I tried this, but I'm doing something wrongly.
Can someone help?
The code is:
CREATE TABLE articles(Article CHAR(35) NOT NULL, Photo_ID INT(5) NOT NULL);
CREATE TABLE photos(Photo_ID INT(5) NOT NULL, Photo_name CHAR(35));
INSERT INTO photos VALUES
(1, "one.png"),
(2, "two.png"),
(3, "three.png");
INSERT INTO articles VALUES
("one", 1),
("two", 2),
("three", 3);
SELECT Photo_name
FROM photos
WHERE articles.Photo_ID = photos.Photo_ID = 2;
And the last select doesn't work. I get error Unknown column 'articles.Photo_ID' in 'where clause'

Your select is wrong you need a proper join and a proer where clause
SELECT photos.Photo_name
FROM photos
INNER JOIN articles ON articles.Photo_ID=photos.Photo_ID
WHERE photos.Photo_ID=2;

You have to write Proper select & join with where clause.
SELECT p.Photo_name
FROM photos p
LEFT JOIN articles a ON a.Photo_ID=p.Photo_ID
WHERE p.Photo_ID = 2;

You are not using the table articles the same way you are using a database, so you can't call articles.Photo_ID in your WHERE clause.
try instead
SELECT Photo_name FROM photos WHERE photos.Photo_ID=2;
and this should work.
when you specity FROM photos, don't call articles.Photo_ID because the table articles is not loaded.
So if you want to load the article table, you should try doing a JOIN ... ON ...
like
SELECT article, photo_name FROM photos LEFT JOIN articles ON articles.Photo_ID = photos.Photo_ID WHERE articles.Photo_ID =2;
Maybe this is new stuff for you, there's 4 type of JOIN : LEFT, RIGHT, INNER, OUTER.
Okay I hope this helps.
Ciao

You need to Join tables.
SELECT p.Photo_name FROM photos p
JOIN articles a
ON p.id = a.Photo_ID
WHERE p.id=2;
P.S Better to change column names
articles
id
article
photo_id
photos
id
name
SELECT p.name FROM photos p
JOIN articles a
ON p.id = a.photo_id
WHERE p.id=2;

Related

MySQL Update rows with double left join, limiting first match

I have three tables (SQLFiddle with tables created)
Orange text is what I need to get by comparing Products.name with Filters.filter.
I figured out that substring match can be done like this:
on Products.name LIKE CONCAT('%',Filters.filter,'%');
I only need the first filter match to be used. So "Mushroom soup" will match "Soup" and not "Mushroom".
What is best approach for this task?
A tested SQLFiddle link, if possible, please.
What I tried: (feel free to skip everything below)
Tried double join:
update Products
left join Filters on Products.name LIKE CONCAT('%',Filters.filter,'%')
join Categories on Filters.category_name = Categories.name
set Products.category_id = Categories.id, Products.filter = Filters.filter;
But this doesn't result in first match being used, because first join returned all filter matches (multiple lines per 1 product), and after second join categories are from last match.
For example: Mushroom soup got into "Mushroom-Ingredients" filter\category, instead of "Soup\Meals".
Also tried join + order by:
select Products.name, Filters.*
from Products
left join Filters on Products.name LIKE CONCAT('%',Filters.filter,'%')
group by Products.name;
This returns the first match per product as I needed, but I can't do a second left-join in the same query, neither can I get "group by" to work after "update" function instead of "select".
For when sqlfiddle collapses:
CREATE TABLE Products
(
`name` varchar(30),
`category_name` varchar (30),
`category_id` int
);
INSERT INTO Products (`name`)
VALUES ('Mushrooms'), ('Can of mushrooms'),
('Can of soup'), ('Mushroom soup'),
('Tomato soup'), ('Tomato');
CREATE TABLE Filters
(
`filter` varchar(30),
`category_name` varchar(30)
);
INSERT INTO Filters (`filter`, `category_name`)
VALUES ('Can of', 'Canned food'), ('Soup', 'Meals'),
('Mushroom', 'Ingredients'), ('Tomato', 'Ingredients');
CREATE TABLE Categories
(
`id` int,
`name` varchar(30)
);
INSERT INTO Categories (`id`, `name`)
VALUES (1, "Canned food"), (2, 'Ingredients'), (3, 'Meals');
You made the task quite easy with your sqlfiddle as well as your attempt to solve the problem with Select query.
It works the way you want I guess and all I need to do is to add one more left join with category table (IDK why you were unable to join Category as it's working properly).
So. I've edited your select query as follows:
select Products.name, Filters.*,Categories.id from Products
left join Filters
on Products.name LIKE CONCAT('%',Filters.filter,'%')
left join Categories
on Categories.name = Filters.category_name
GROUP BY Products.name;
You will get the results you want with this query.
Now, in order to update Products table with the result of this query, you can do following:
update Products
left join Filters
on Products.name LIKE CONCAT('%',Filters.filter,'%')
left join Categories
on Categories.name = Filters.category_name
set Products.category_name = Filters.category_name,
Products.category_id = Categories.id;
Click here for Working Demo
Hope it helps!

Understanding joins: Listing categories without specified product

I am new to MySQL, trying to understand how join works.
I have two tables: categories and products each with id and name fields.
Third table is product_to_category (many to many) with category_id and product_id.
I wanted to list all categories containing specified product (with id = 5).
To do this, I made a following query:
SELECT `categories`.`id`, `categories`.`name`
FROM `categories`
LEFT JOIN `product_to_category`
ON `categories`.`id` = `product_to_category`.`category_id`
WHERE `product_to_category`.`product_id` = 5
I am wondering if it is possible to something opposite: to list all categories which does not contains specified product?
Many thanks!
I'd use the not exists operator for this:
SELECT c.`id`, c.`name`
FROM `categories` c
WHERE NOT EXISTS (SELECT *
FROM `product_to_category` ptc
WHERE c.`id` = ptc.`category_id` AND
ptc.`product_id` = 5)

How to select photos that are in selected albums

I couldn't figure out how to word this question, so I'm going to use a typical example. Imagine I have a common design for photo albums with 3 tables - photos, albums, photo_albums (a lookup table). Albums names are A, B and C.
How would I find all photos that are in both A and C?
If your table design look like this and you want to filter on the album name. Then you can do this:
SELECT
*
FROM
photos
WHERE EXISTS
(
SELECT
NULL
FROM
photo_albums
JOIN albums
ON photo_albums.albumId=albums.albumId
WHERE
photos.photoId=photo_albums.photoId
AND albums.albumName IN ('A','B')
)
I think the better solution would be to do it via the Id if you have one. Like this:
SELECT
*
FROM
photos
WHERE EXISTS
(
SELECT
NULL
FROM
photo_albums
WHERE
photos.photoId=photo_albums.photoId
AND photo_albums.albumId IN (1,3)
)
You first do a JOIN between albums and photo_albums and do a GROUP BY to find which photos are there in both albums A and C. Then JOIN this result with photos to get the final result.
Something like this:
SELECT p.*
FROM photos p
JOIN (
SELECT pa.photoid
FROM albums a
JOIN photos_albums pa
ON a.albumid = pa.albumid
WHERE a.albumname IN ('A','C')
GROUP BY pa.photoid
HAVING COUNT(DISTINCT(pa.albumid)) = 2
) t
ON p.photoid = t.photoid
Working Fiddle: http://sqlfiddle.com/#!2/187f36/20

SQL Query Assistance Needed - Joining Information

I have two tables, one holds a primary key "id" and "web_id" which holds a varchar of a url, table is named "SG".
In the other table has a primary key "id", "web_id", "votetype" and "userID". Table is named "Votes".
I want to end up with a table that when I search for "www.facebook.com"
I get a single row table with this:
Column names: web_id, likes, dislikes
Where likes is votetype = 0, and dislikes is votetype = 1.
Here is what I have however I am inexperienced with Joins and the error messages don't give any information.
SELECT web_id
FROM `SG`
WHERE web_id="www.facebook.com"
LEFT OUTER JOIN
(SELECT COUNT(*) AS likes
FROM `Votes` WHERE web_id="www.facebook.com" AND votetype=0
LEFT OUTER JOIN
(SELECT COUNT(*) AS dislikes
FROM `Votes`
WHERE web_id="www.facebook.com" AND votetype=1
)
)
Any help with correcting my sql would be greatly appreciated! :)
I guess you need the count of likes and dislikes of single web_id
SELECT s.web_id ,SUM(v.votetype = 0) AS `likes` ,SUM(v.votetype = 1) AS `dislikes`
FROM `SG` s
INNER JOIN Votes v ON (s.web_id =v.web_id )
WHERE s.web_id='www.facebook.com'
GROUP BY v.web_id

Join on multiple rows

I'm trying to load rows form a posts table based on whether they have multiple rows in another table. Take the below table structures:
posts
post_id post_title
-------------------
1 My Post
2 Another Post
post_tags
post_tag_id post_tag_name
--------------------------
1 My Tag
2 Another Tag
postTags
postTag_id postTag_tag_id postTag_post_id
------------------------------------------
1 1 1
2 2 1
Unsurprisingly, post and post_tags stores the posts and tags, and postTags joins which posts have which tags.
What I'd normally do to join the tables is this:
SELECT * FROM (`posts`)
JOIN `postTags` ON (`postTag_post_id` = `post_id`)
JOIN `post_tags` ON (`post_tag_id` = `postTag_tag_id`)
Then I'd have information on the tags, and can have additional stuff later in the query to search tag names for search terms etc, and then GROUP once I have posts that match the search terms.
What I'm trying to do is only select from posts where a post has both tag 1 AND tag 2, and I can't work out the SQL for it. I think it needs to be done in the actual JOIN rather than having a WHERE clause for it as when I run the join above I'd obviously get two rows back, so I can't have something like
WHERE post_tag_id = 1 AND post_tag_id = 2
as each row will only have one post_tag_id, and I can't check different values for the same column in one row.
What I've tried to do is something like this:
SELECT * FROM (`posts`)
JOIN `postTags` ON (postTag_tag_id = 1 AND postTag_tag_id = 2)
JOIN `post_tags` ON (`post_tag_id` = `postTag_tag_id`)
but this is returning 0 results when I run it; I've put conditions like this in JOINS before for similar things and I'm sure it's close but can't quite work out what to do if this doesn't work.
Am I at least on the right track? Hopefully I'm not missing something obvious.
Thanks.
You are trying to ask the postTags row to be at the same time one thing and another.
You either need to do two joins to post_tags and postTags so you get both. Or you can say that the post can have whatever tag between those two and the total amount of tags must equal two (assuming a post cannot related to the same tag more than once).
First approach:
SELECT *
FROM `posts` as p
WHERE p.`post_id` IN (SELECT pt.`postTag_post_id`
FROM `postTags` as pt
WHERE pt.`postTag_tag_id` = 1)
AND p.`post_id` IN (SELECT pt.`postTag_post_id`
FROM `postTags` as pt
WHERE pt.`postTag_tag_id` = 2);
Second approach:
SELECT *
FROM posts as p
WHERE p.post_id IN (SELECT pt.postTag_post_id
FROM (SELECT count(0) as c, pt.postTag_post_id
FROM postTags as pt
WHERE pt.postTag_tag_id IN (1, 2)
GROUP BY pt.postTag_post_id
HAVING c = 2) as pt);
I want also to add that if you use IN or EXISTS in the first approach then you won't have multiple lines for the same post row just because you have more than one tag. This way you save one DISTINCT later that would make your query slower.
I've used an IN in the second approach just as a rule of thumb I use: if you don't need to show the data you don't need to do a JOIN in the FROM section.
SELECT p.*, t1.*, t2.* FROM posts p
INNER JOIN postTags pt1 ON pt1.postTag_post_id = p.id AND pt1.postTag_tag_id = 1
INNER JOIN postTags pt2 ON pt2.postTag_post_id = p.id AND pt2.postTag_tag_id = 2
INNER JOIN post_tags t1 ON t1.post_tag_id = pt1.postTag_tag_id
INNER JOIN post_tags t2 ON t2.post_tag_id = pt2.postTag_tag_id
Without actually building a db the same as yours this is hard to verify but it should work.
Let me start by saying that this type of query is much easier and much more performant in a database that supports analytic queries (Oracle, MS SQL Server). So in MySQL you have to do it the old, crappy, aggregate way.
I also want to say that having a table that stores the names of the tags in post_tags and then the mapping of post tags to posts in postTags is confusing. If it were me, I would change the name of the mapping table to post_tags_map or post_tags_to_post_map. So you would have posts with post_id, post_tags with post_tags_id, and post_tags_map with post_tags_map_id. And those id columns would be named the same in every table. Having the same column that is named differently in other tables is also confusing.
Anyways, let's solve your problem.
First you want a result set that is 1 post id per row, and only the posts that have tags 1 & 2.
select postTag_post_id, count(1) cnt from (
select postTag_post_id from postTags where postTag_tag_id in (1, 2)
) group by postTag_post_id;`
That should give you back data like this:
postTag_post_id | cnt
1 | 2
Then you can join that result set back to your posts table.
select * from posts p,
(
select postTag_post_id, count(1) cnt from (
select postTag_post_id from postTags where postTag_tag_id in (1, 2)
) group by postTag_post_id;
) t
where p.post_id = t.postTag_post_id
and t.cnt >= 2;
If you need to do another join to the post_tags table in order to get the postTag_tag_id from the post_tag_name, your inner most query would change like so:
select postTag_post_id
from postTags a,
post_tags b
where a.postTag_tag_id = b.post_tag_id
and b.post_tag_name in ('tag 1', 'tag 2');
That should do the trick.
Assuming you already know tag IDs (1 and 2), you could do something like this:
SELECT post_id, post_title
FROM posts JOIN postTags ON (postTag_post_id = post_id)
WHERE postTag_tag_id IN (1, 2)
GROUP BY post_id, post_title
HAVING COUNT(DISTINCT postTag_tag_id) = 2
NOTE: DISTINCT is not necessary if there is an alternate key on postTags {postTag_tag_id, postTag_post_id}, as it should be.
NOTE: If you don't have tag IDs (and just have tag names), you'll need another JOIN (towards the post_tags table).
BTW, you should seriously consider ditching the surrogate PK in the junction table (postTags.postTag_id) and just having the natural PK {postTag_tag_id, postTag_post_id}. InnoDB tables are clustered, and secondary indexes in clustered tables are fatter and slower than in heap-based tables. Also, some queries can benefit from storing posts tagged by the same tag physically close together (or storing tags of the same post close together, if you reverse the PK).