MySQL, UNION ALL with SQL_CALC_FOUND_ROWS not working sometimes - mysql

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

Related

How to inner join a lookup table for a mysql query?

I need help with a mysql query using the following two tables:
profiles (TABLE 1)
id user_id gender age height bodytype
1 1 1 57 1 2
2 2 2 32 2 1
profile_lookup (TABLE 2)
id option_group option_value option_name
1 gender 1 Female
2 gender 2 Male
3 gender 3 Prefer not to say
4 height 1 5 ft - 6 in
5 height 2 5ft - 9 in
6 bodytype 1 Petite/slim
7 bodytype 2 Average
There are whole lot of other options and option values that i am omitting for the sake of brevity
I am interested to do inner join queries using the syntax as shown below:
SELECT *
FROM profiles
WHERE bodytype = 2
JOIN profile_lookup
ON profiles.gender = profile_lookup..... (not sure)
Request help with using the correct syntax using the above two tables. Thanks
I think you want:
SELECT p.*, plg.option_name as gender
FROM profiles p INNER JOIN
profile_lookup plg
ON plg.option_group = 'gender' and
plg.option_value = p.gender
WHERE p.bodytype = 2 ;
You can extend this to other columns. You might want a LEFT JOIN in case some values don't match (i.e. are NULL):
SELECT p.*, plg.option_name as gender, plh.option_name as height
FROM profiles p LEFT JOIN
profile_lookup plg
ON plg.option_group = 'gender' AND
plg.option_value = p.gender LEFT JOIN
profile_lookup plh
ON plh.option_group = 'height' AND
plh.option_value = p.height
WHERE p.bodytype = 2
The where clause should be after the JOIN clause
SELECT * FROM profiles
INNER JOIN profile_lookup ON profiles.gender = profile_lookup.option_value
and profile_lookup.option_group = 'gender'
WHERE profiles.bodytype = 2
and for the join you need the proper profile_lookup.option_value

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 - Join three tables (in a row?)

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

Conditionals and counts in SQL

I have a MySQL table
"ratings", with
an ID column
a column called like_dislike (holds either null, 0, or 1),
and a column called lesson_id (a foreign key from lessons).
the MySQL table, "lessons", with
an ID column
a teacher_id column
I need to select this information:
"SELECT r.like_dislike FROM ratings r INNER JOIN lessons l on l.lesson_id = r.lesson_id";
However, this actually part of a much larger SQL statement, and what I would like to do is:
Foreach lesson_id, if like_dislike == 0, SELECT count(like_dislike) as like
AND
Foreach lesson_id, if like_dislike == 1, SELECT count(like_dislike) as dislike
I do not know how to turn this pseudo code into SQL. I also need to do this in SQL, rather than in something like PHP, because it is part of a larger SQL statement whose conversion into properly formatted arrays is deeply troubling.
You should be able to accomplish this with grouping. For example:
SELECT r.lesson_id, COUNT(*) AS like
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
WHERE r.like_dislike = 0
GROUP BY r.lesson_id;
The same for dislike, just change the WHERE clause to
WHERE r.like_dislike = 1
EDIT:
This can be combined into one query as requested, by adding another level of grouping:
SELECT r.lesson_id, r.like_dislike, COUNT(*) AS count
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike;
This will give you output, for example:
+-----------+--------------+-------+
| lesson_id | like_dislike | count |
+-----------+--------------+-------+
| 1 | 0 | 12 |
| 1 | 1 | 7 |
| 2 | 0 | 1 |
| 2 | 1 | 4 |
+-----------+--------------+-------+
so for lesson_id of 1, there are 12 likes, and 7 dislikes, etc...
EDIT 2:
To get one row for each lesson_id, you can modify the statement a little:
SELECT r.lesson_id,
CASE WHEN r.like_dislike = 0 THEN COUNT(*) END AS like,
CASE WHEN r.like_dislike = 1 THEN COUNT(*) END AS dislike
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike;
For that matter, you don't even need to join on the lessons table at all, unless you are somehow getting ratings that do not link to a lesson. If you want to include lessons that have no ratings, then you will have to change to an OUTER join:
SELECT l.lesson_id,
CASE WHEN r.like_dislike = 0 THEN COUNT(*) END AS like,
CASE WHEN r.like_dislike = 1 THEN COUNT(*) END AS dislike
FROM lessons l
LEFT JOIN ratings r ON r.lesson_id = l.lesson_id
GROUP BY l.lesson_id, r.like_dislike;
Maybe you can use a case statement, something like this:
SELECT r.lesson_id,
case when r.like_dislike == 0 then (count(*)) end as like,
case when r.like_dislike == 1 then (count(*)) end as dislike
FROM ratings r INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike
I haven't tested it, but you can see the idea. Further, you must set a case to count 1's and another to count 0's because like_dislike can be null.
try this
SELECT if(r.like_dislike =0 ,count(like_dislike) as like , if( r.like_dislike =1 , count(like_dislike) as dislike, 'its null'))
FROM ratings r
INNER JOIN lessons l on l.lesson_id = r.lesson_id
if you are adding condition on yr datatable then code it like below:
Declare #Counter int
Set #Counter=(SELECT Count(Student_ID) as 'StudentCount' FROM tbCourseSemOne
where Student_ID=1 Having Count(Student_ID) < 6 and Count(Student_ID) > 0)
if(#Counter <6)
print'Sorry! You cannot add more than five subject data for a single stduent'
else
print'Insert Code'
hope it helps

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;