MYSQL - Join three tables (in a row?) - mysql

I am having trouble with coming up with the query required to do what I am after.
I have three tables like this:
client_files
-----------------------
client_id file_id
-----------------------
1 2
1 3
1 6
2 1
2 2
3 5
files
-------------------------------------------------
ID file_name file_category_id
-------------------------------------------------
1 file1.ext 1
2 file2.ext 3
3 file3.ext 1
4 file4.ext 1
5 file5.ext 2
6 file6.ext 2
file_categories
--------------------------
ID category_name
--------------------------
1 category1
2 category2
3 category3
I am attempting to build a query which will return the filename and category name for a particular client ID.
The result I am expecting is (from ID 1):
-----------------------------
file_name category_name
-----------------------------
file2.ext category3
file3.ext category1
file6.ext category2
As far as I understand it, I need to join the client_files table to the files table and then to the file_categories table. I've looked at other examples which are about joining two tables with a 3rd table linking them together, but believe this is a different situation.
This is the equivalent of what I have come up with, but the results are almost random, with some files returned that are not linked, and some are missing.
SELECT
f.file_name,
fc.category_name
FROM
client_files cf,
files f,
file_categories fc
WHERE
cf.client_id = 1 AND f.ID = cf.file_id AND fc.ID = f.file_category_id;

You can do it in two ways, but i thing essentially it's the same.
The first way:
SELECT
f.file_name,
fc.category_name
FROM
client_files cf
JOIN files f ON cf.file_id = f.id
JOIN file_categories fc ON fc.id = f.file_category_id
Or, the second way:
SELECT
f.file_name,
fc.category_name
FROM
(client_files cf
JOIN files f ON cf.file_id = f.id) data1
JOIN file_categories fc ON fc.id = data1.file_category_id

You need to have proper join conditions, right now you are doing cross join and then having the where condition.
SELECT
cf.client_id,
f.file_name,
fc.category_name
FROM
client_files cf
LEFT JOIN
files f ON cf.file_id = f.ID
LEFT JOIN
file_categories fc ON fc.ID = f.file_category_id
WHERE
cf.client_id = 1;

Try this query:
SELECT files.file_name, file_categories.category_name
FROM ((files LEFT JOIN client_files ON files.ID = client_files.file_id)
LEFT JOIN file_categories ON files.file_category_id = file_categories.ID)
WHERE client_files.client_id = 1

Related

Make Mysql count even if value is equal to zero

I'm trying to count number of items by categories and i want to show 0 on categories with no items
My actual code doen't consider categories with 0 value
SELECT categories.categories_id, categories_name, categories_active, categories_status,
COUNT(IFNULL( product.product_id,0)) as count
FROM categories
LEFT JOIN product ON product.categories_id = categories.categories_id
WHERE categories_status = 1 AND product.active = 1
GROUP BY categories.categories_id
suppose we have two tables categories and products
cat_id cat_name
1 cat A
2 cat B
3 cat C
product_id product_name cat_id
1 prod A 1
2 prod B 2
3 prod C 1
4 prod X 2
i expect to get
cat_id cat_name count products
1 cat A 2
2 cat B 2
3 cat C 0
The problem is that you use
AND product.active = 1
and you filter out null values (categories without products) because of this condition. Try like this:
AND (product.active = 1 OR product.active IS NULL)
final query:
SELECT categories.categories_id, categories_name, categories_active, categories_status,
COUNT(product.product_id) AS count
FROM categories
LEFT JOIN product ON product.categories_id = categories.categories_id
WHERE categories_status = 1 AND (product.active = 1 OR product.active IS NULL)
GROUP BY categories.categories_id
Try using COUNT(*). It counts every row generated by your from/ join / where clauses.
Instead of left join use right join and change the COUNT(IFNULL( product.product_id,0)) to COUNT(product.product_id).Try this once.
SELECT categories.categories_id, categories_name, categories_active, categories_status,
COUNT(product.product_id) as count
FROM categories
RIGHT JOIN product ON product.categories_id = categories.categories_id
WHERE categories_status = 1 AND product.active = 1
GROUP BY categories.categories_id
By right join you will get count 0 data also

SQL query from many to many relation

I have many to many relation
table: images
id imageName
1 pic01
2 pic02
3 pic03
table: imagesKeywords
imageId keywordId
1 2
1 3
1 4
2 3
3 1
3 4
3 2
table: keywords
id keywordName
1 car
2 tree
3 cat
4 phone
Each image has some keywords, and different images can have the same keyword.
I need to make a search for images , they have a specific keywordName's.
example-1: search for car and phone
the result should be : pic03
example-2: search for tree and phone
the result should be : pic01, pic03
You appear to want JOIN with GROUP BY Clause :
select i.imageName
from images i inner join
imagesKeywords ik
on ik.imageId = i.id inner join
keywords k
on k.id = ik.keywordId
where k.keywordName in ('car', 'phone')
group by i.imageName
having count(*) = 2;
As I understand you, this should work:
select i.imageName as name from keywords k
join imagesKeywords iK on k.id = iK.keywordId
join images i on iK.imageId = i.id
group by i.imageName;
One possible solution,
with subquery as
(select i1.imageName, k1.keywordName from keywords k1 join imagekeywords ik1 on k1.id=ik1.keywordId join images i1 on i1.id = ik1.imageId )
select a.imageName from subquery a join subquery b on a.imageName=b.imageName where a.keywordName ='car' and b.keywordName='phone';

MySQL, UNION ALL with SQL_CALC_FOUND_ROWS not working sometimes

I'm having some problems with UNION ALL using SQL_CALC_FOUND_ROWS in MySQL, InnoDB. First let me show a simple database schema to explain what's going on...
Database Schema
Media Table
media_id title
-------------------------------------
1 Empire of the Sun
Genre Pattern Table
genre_id name
-------------------------------------
1 Action
2 Adventure
Locale Table
locale_id code
-------------------------------------
1 en_US
2 pt_BR
3 fr_FR
Genre Translations Table
genre_id locale translation
-------------------------------------
1 1 Action
1 2 Ação
2 1 Adventure
2 2 Aventura
Media Genres Table
media_id genre_id
-------------------------------------
1 1
1 2
Now it's simple: I need to return a genre name compatible with user locale. Let's suppose that user locale is pt_BR. To I get results I use the follow SQL:
select gt.name
from media_genres mg
inner join genre_translation gt on (mg.genre_id = gt.genre_id)
where (mg.media_id = 1 and gt.locale_id = 2)
It returns:
name
-------------------------------------
Ação
Aventura
But, now, let's suppose that user locale is fr_FR. There's no translation for fr_FR, right? The solution I found was use a UNION ALL with SQL_CALC_FOUND_ROWS, then I use the following SQL:
select SQL_CALC_FOUND_ROWS gt.name
from media_genres mg
inner join genre_translation gt on (mg.genre_id = gt.genre_id)
where (mg.media_id = 1 and gt.locale_id = 3)
UNION ALL
select gt.name
from media_genres mg
inner join genre_translation gt on (mg.genre_id = gt.genre_id)
where FOUND_ROWS() = 0 and (mg.media_id = 1 and gt.locale_id = 1)
If there's no found rows for locale_id = 3 and media_id = 1, then I'll get the results for locale_id = 1 (default) and media_id = 1.
The problem is: the statement sometimes return right data and sometimes return no data.
And my main question: why is this happening?
The way to accomplish this is by using LEFT JOIN to join with the translation table, and then use IFNULL to supply a default when there's no match.
SELECT mg.genre_id, IFNULL(gt2.name, gt1.name) AS name
FROM media_genres AS mg
JOIN genre_translation AS gt1 ON mg.genre_id = gt.genre_id AND gt.locale_id = 1
LEFT JOIN genre_translation AS gt2 ON mg.genre_id = gt.genre_id AND gt.locale_id = 3
WHERE mg.media_id = 1

join or select in on multiple fields

(The example that follows is hypothetical, but illustrates the concept).
Using MySQL, say I have 2 tables:
userFromID userToId moreInfo
1 2 cat
1 3 dog
4 1 bear
3 4 fish
And...
userId someInfo addlInfo
1 m 32
2 f 33
3 m 25
4 f 28
And I want to query for a user id, and get back joined info from both tables for all users that share a relationship with user1.
assume that the first table has something like alter table thatFirstTable add unique index(userFromId, userToId) so there won't be any duplicates - each relationship between the two ids will be unique.
it doesn't matter who's the "from" or "to"
so the desired result would be something like this, if queried for relationships with user id: 1
userId moreInfo someInfo addlInfo
2 cat f 33
3 dog m 25
4 bear f 28
Thanks.
/EDIT this "works" but I suspect there's a better way?
SELECT * FROM users JOIN friends ON friends.userFrom = users.id OR friends.userTo = users.id WHERE users.id != 1 AND friends.userFrom = 1 OR friends.userTo = 1
/EDIT2 - I updated the sample output to better reflect the goal
try this query::
select tbl2.userid,tbl1.moreinfo,
tbl2.someinfo,tbl2.addinfo
from tbl1 join tbl2
on (tbl1.usertoid = tbl2.userid and tbl1.userfromid = 1)
You should just join the tables with the query below.
select u.userId, f.moreInfo, u.someInfo, u.addlInfo
from users AS u INNER JOIN friends AS f ON u.userId = f.UserToId
where f.userFrom = 1
Try this. Tested and 100% working
select a.userToID, a.moreInfo, b.someInfo, b.addInfo from tbl1 a
left outer join
tbl2 b on a.userToID = b.userId
where a.userFromID = 1;

MySQL how to select multiple attributes from a Cartesian product?

I'm trying to get multiple attributes (e.g bedrooms, bathrooms, etc) from a few tables yes I can only seem to get one attribute with my current query. What am I missing?
What I am ultimately trying to find is property_id's with 2 bathrooms and 2 bedrooms. In this case, my desired results are property id's 1 and 4.
Here's my example on sqlfiddle
Example tables:
property
--------
id
1
2
3
4
attribute
---------
id name
1 bedrooms
2 bathrooms
attribute_value
---------------------
id attribute_id value
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 3
property_attributes
------------------------------
property_id attribute_value_id
1 2
1 5
2 2
2 4
3 1
3 4
4 2
4 5
This query can produce the results I want if I only want to see a query based on single attribute:
SELECT p.property_id
FROM property p
INNER JOIN property_attribute pa ON p.property_id = pa.property_id
INNER JOIN property_area pc ON p.property_id = pc.property_id
WHERE pa.attribute_value_id
IN (
SELECT av.attribute_value_id
FROM attribute_value av
INNER JOIN attribute a ON av.attribute_id = a.attribute_id
WHERE a.name like 'bedrooms' AND av.value like '2')
But what must I do to get results with more attributes (e.g. WHERE a.name like 'bathrooms' AND av.value like '2')? If I add another subquery in the main where clause, it doesn't return any results.
/// EDIT ///
here is the solution that worked for me if anyone else winds up in such a situation:
SELECT p.property_id
FROM property p
INNER JOIN property_attribute pa ON p.property_id = pa.property_id
INNER JOIN property_attribute pa2 ON p.property_id = pa2.property_id
INNER JOIN property_attribute pa3 ON p.property_id = pa3.property_id
WHERE pa.attribute_value_id = 2
AND pa2.attribute_value_id = 5
AND pa3.attribute_value_id = 7
This makes usage of a self-join.
You have an awful lot of indirection in your schema. Is there any particular point why you need the property_attribute table? Do you expect a single attribute value to be shared by many properties?
In any case, here is the query for you. Very much in the spirit of the two possible duplicates I mentioned above.
SELECT property.property_id,
bedrooms.value AS bedrooms,
bathrooms.value AS bathrooms
FROM property,
attribute AS bedrooms_attr,
attribute_value AS bedrooms,
property_attribute AS bedrooms_prop,
attribute AS bathrooms_attr,
attribute_value AS bathrooms,
property_attribute AS bathrooms_prop
WHERE bedrooms_attr.name = 'bedrooms'
AND bedrooms.attribute_id = bedrooms_attr.attribute_id
AND bedrooms.attribute_value_id = bedrooms_prop.attribute_value_id
AND bedrooms_prop.property_id = property.property_id
AND bedrooms.value = 2
AND bathrooms_attr.name = 'bathrooms'
AND bathrooms.attribute_id = bathrooms_attr.attribute_id
AND bathrooms.attribute_value_id = bathrooms_prop.attribute_value_id
AND bathrooms_prop.property_id = property.property_id
AND bathrooms.value = 2
Thanks for providing a fiddle with data to play with.