Search table based on linked text field from second table - mysql

Not being an SQL wiz at all, browsing through the results here, I was unable to find a proper solution, although the problem itself is fairly simple.
Two tables:
products
id
title
strings
id
language_en
An entry in products may look like this:
id = 1
title = 10
Corresponding entry in strings:
id = 10
language_en = "myProduct"
Where as the number in products.title contains the id of a corresponding entry in strings.
I want to search products for a certain title, but those titles are stored in strings (because of multi-language capacities).
This is what i came up with:
SELECT p.* FROM products p JOIN strings s ON p.title LIKE s.language_en WHERE s.language_en LIKE "myProduct"
Unfortunately, this does not yield any proper results. Obviously i have trouble understanding the concept behind the join, but even reading some articles on the concept does not get me there. It's not my pair of shoes.

In your sql condition may be wrong i think, i modified that sql.
select p.* FROM products p JOIN strings s ON p.title = s.id and s.language_en = "Product name"
you can execute and see the result.
Thank you.

The join is wrong - it's comparing p.title to s.language_en instead of s.id which you described should match it.
Also note that when used without a wildcard, the like operator is essentially equivalent to =, and it may make the code easier to read if you use it directly:
SELECT p.*
FROM products p
JOIN strings s ON p.title = s.id
WHERE s.language_en = 'myProduct'

Related

MySQL cross reference table join

I am pretty new to sql and searching the forum did not give me the right answer, so I place a new one. I have three tables:
Products
product_id
product_description
Keywords
keyword_id
Keyword_description
And a reference table keywords_to_products:
reference_id
keyword_id-> relates to keywords table
product_id -> relates to products table
I am looking for two queries :
One that gives all products matching one OR more specific keywords (I think I already have this)
ie: all products having the keywords 'bolt' OR 'screw'
One that gives all products matching multiple keywords (AND) this is the one I don't get...
ie: all products having the keywords 'bolt' AND 'nut'
Although I think it is not the most exciting thing to do, I just cannot figure it out.
Besides, I am wondering if I do the right thing anyway.
I am writing an application having appr. 1.000.000 different products in it with an article number and a description. I add important words from the description to the keyword database and also add additional keywords like EAN number or equivalent productnumber which are not in the product table.
These are all connected through the keyword-to-product table.
Every keyword, even EAN numbers for example, can refer to multiple products.
The idea is searching through the keyword_to_product_table only instead of searching through multiple fields in the products table. Good idea ??
Thanks in advance for your replies..
Regards, Arno Verkley
Is this what you want?
select distinct p.Id, p.Description
from Product p
join ProductKeyword pk
on p.id = pk.ProductId
join Keyword k
on k.id = pk.KeyWordId
and k.description in ("Red", "Yellow")
http://sqlfiddle.com/#!2/b6053/3
Searching directly in your main table, say in the description column, you would have a where looking like this
WHERE
description like "%bolt%"
and description like "%nut%"
Now using a keyword table (product_id, keyword), I can suggest
SELECT
DISTINCT(p.*)
FROM Product p
JOIN ProductKeyword pk1
ON p.id = pk1.productId
JOIN ProductKeyword pk2
ON p.id = pk2.productId
WHERE
pk1.keyword = "bolt"
AND
pk2.keyword = "nut"
And yes, a keyword_to_product table sounds a good idea. You can use an index on keyword, that will keep your queries fast (when the 'like' operator will slow down a lot queries on a big table).
First of all, thanks a lot for the answers !
A little bit of both answers seem to do the trick (on the fiddle; by the way thanks for this very usefull trial and error tool, although it is not very fast ;o( )
SELECT distinct p.id, p.descriptionFROM Product p
JOIN ProductKeyword pk1
ON p.id = pk1.ProductId
join Keyword k1
on k1.id = pk1.KeyWordId
JOIN ProductKeyword pk2
ON p.id = pk2.ProductId
join Keyword k2
on k2.id = pk2.KeyWordId
WHERE
k1.description = "Yellow"
AND
k2.description = "Red"
With indexes on the id's and keyword.description will this be the fastest method ?? And what if I will be using more then two keywords ?? just add more joins and where's ??
Regards, Arno

MySQL joins confuse me and I always get syntax errors

I need to fetch some data from a MySQL db. I have the function working using two separate, simple queries, but i'm sure this is probably achievable with one query using a JOIN. SQL isn't my strong point and I keep getting syntax errors when I try. Sure, I could leave it as it is, why not, it works. But i would prefer to see a real world example of how they can be joined so i can try and apply it on other queries in the future.
Query one is:
select manufacturers_id from products where products_name = product a
The result is then put into a variable and used in the following
select manufacturers_name from manufacturers where manufacturers id = $man_id
So, basically, the products table holds the manufacturers_id, which we need to collect for a given product to find out what the manufacturers name is from the manufacturers table.
manufacturers_id is the common field.
Try this:
SELECT b.manufacturers_name
FROM products a
LEFT JOIN manufacturers b ON a.manufacturers_id = b.manufacturers_id
WHERE a.products_name = 'PRODUCT NAME'
Below is a diagram of SQL Joins
Image Source
Try this:
select m.manufacturers_name from products p, manufacturers m
where p.products_name = 'productA' and p.manufacturers_id = m.id
I believe this is what you're looking for
SELECT manufacturers.manufacturers_name
FROM products
JOIN manufacturers on manufacturers_id=products.manufacturers_id
WHERE manufacturers_name='product a'
With a join, you just need to follow this syntax, and remember that you have to set the matching columns equal to each other so you don't end up with invalid rows.
I'd show you how the equivalent of this using table joins, but I really don't want to encourage the use of that syntax, even by accident.

Select a post that does not have a particular tag

I have a post/tag database, with the usual post, tag, and tag_post tables. The tag_post table contains tagid and postid fields.
I need to query posts. When I want to fetch posts that have a certain tag, I have to use a join:
... INNER JOIN tag_post ON post.id = tag_post.postid
WHERE tag_post.tagid = {required_tagid}`
When I want to fetch posts that have tagIdA and tagIdB, I have to use two joins (which I kind of came to terms with eventually).
Now, I need to query posts that do not have a certain tag. Without much thought, I just changed the = to !=:
... INNER JOIN tag_post ON post.id = tag_post.postid
WHERE tag_post.tagid != {certain_tagid}`
Boom! Wrong logic!
I did come up with this - just writing the logic here:
... INNER JOIN tag_post ON post.id = tag_post.postid
WHERE tag_post.postid NOT IN
(SELECT postid from tag_post where tagid = {certain_tagid})
I know this will work, but due to the way I've been brought up, I feel guilty (justified or not) whenever I write a query with a subquery.
Suggest a better way to do this?
You can think of it as "find all rows in posts that do not have a match in tags (for a specific tag)"
This is the textbook use case for a LEFT JOIN.
LEFT JOIN tag_post ON post.id = tag_post.postid AND tag_post.tagid = {required_tagid}
WHERE tag_post.tag_id IS NULL
Note that you have to have the tag id in the ON clause of the join.
For a reference on join types, see here: http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
In addition to Gavin Towey's good answer, you can use a not exists subquery:
where not exists
(
select *
from tag_post
where post.id = tag_post.postid
and tag_post.tagid = {required_tagid}
)
The database typically executes both variants in the same way. I personally find the not exists approach easier to read.
When I want to fetch posts that have tagIdA and tagIdB, I have to use two joins (which I kind of came to terms with eventually).
There are other ways.
One can obtain all the id of all posts that are tagged with both tagid 123 and 456 by grouping filtering tag_post for only those tags, grouping by post and then dropping any groups that contain fewer tags than expected; then one can use the result to filter the posts table:
SELECT * FROM posts WHERE id IN (
SELECT postid
FROM tag_post
WHERE tagid IN (123,456)
GROUP BY postid
HAVING COUNT(*) = 2
)
If a post can be tagged with the same tagid multiple times, you will need to replace COUNT(*) with the less performant COUNT(DISTINCT tagid).
Now, I need to query posts that do not have a certain tag.
This is known as an anti-join. The easiest way is to replace IN from the query above with NOT IN, as you proposed. I wouldn't feel too guilty about it. The alternative is to use an outer join, as proposed in #GavinTowey's answer.

Select columns from various tables in mySQL

I have a dynamic list of categories, which is retrieved from mySQL. When the user is clicking on a category, it loads images for that specific category from the DB. This is the function that does it:
function getImages($categoryID)
{
return json_encode(select("SELECT * FROM images WHERE categoryID=$categoryID"));
}
Now I also want to retrieve also the category name again from the "categories" table (to include the name of the selected category in other part of the page). I tried to do this:
function getImages($categoryID)
{
return json_encode(select(
"SELECT categories.categoryName, images.imageFileName, images.imageHeader
FROM images JOIN categories
ON categories.categoryID = images.categoryID
WHERE categoryID=$categoryID"));
}
but that didn't work, now I don't even get the images.
How can it be done?
In your WHERE clause, categoryID is ambiguous.
WHERE categoryID=$categoryID
Since you have two tables with categoryID, you need to identify which one the WHERE should be looking at.
WHERE categories.categoryID=$categoryID
To make life easier for yourself, you might want to consider aliasing your tables, like this:
SELECT c.categoryName, i.imageFileName, i.imageHeader
FROM images i
JOIN categories c ON c.categoryID = i.categoryID
WHERE c.categoryID=$categoryID
Also, which language are you developing in? If this is a web app, you might want to consider escaping $categoryID to protect against SQL injection.
There is an error in the SQL query, categoryID is ambiguous. Try:
SELECT
categories.categoryName,
images.imageFileName,
images.imageHeader
FROM
images
JOIN
categories on categories.categoryID = images.categoryID
WHERE
categories.categoryID=$categoryID;
Rehashing what's already been said, try a left join so that images that aren't categorized don't get filtered out and use an escape such as the following:
$query = "SELECT c.categoryName, i.imageFileName, i.imageHeader
FROM images i
LEFT JOIN categories c
ON c.categoryID = i.categoryID
WHERE c.categoryID= " . intval($categoryID) . ";"
I think you must qualify the field in your WHEREclause with a table name, i.e.
WHERE image.categoryID=$categoryID
The databse engine does not know which of the two categoryID fields you mean. If you run into problems like this again, you should test the query on the MySQL command line. There you get an error message, that is very helpful in fixing your error.

Rewriting query in order to remove FIND_IN_SET?

My mysql query looks like this:
SELECT pages.*,
showcase.*,
project.*
FROM pages
INNER JOIN showcase ON showcase.pid = pages.uid AND showcase.deleted != 1
INNER JOIN project ON FIND_IN_SET(project.uid, showcase.projects)
WHERE pages.deleted != 1
AND pages.pid = 14
AND pages.dokType = 150
The problem is the second INNER JOIN - it uses FIND_IN_SET because a showcase (= collection of projects) stores its projects as a comma separated list in the field showcase.projects. FIND_IN_SET cant use indexes as far as I know, so the second join requires a full table scan of the project table. Any possibility to use an index without changing the database scheme?
You searching for a 'string' inside another string. You'll have to do a scan regardless. The other optimized way to do this using indexes is to use a join table. Create a new table showcase_projects that has columns of project_id and showcase_id. This will then have a record for every association between the two.
Now this answer is based on my primitive understanding of your datastructure from this query.
Is showcase.projects a VARCHAR? Then you could use REGEXP like this:
...
INNER JOIN project ON
showcase.projects REGEXP CONCAT('[[:<:]]', project.uid, '[[:>:]]')
WHERE ...
This expression looks for whole words only (the bracket constructions mark left and right word boundaries, respectively. However, I'm not sure this will be able to make use of an index, maybe someone can come up with something smarter.