I have two tables and need to get all rows from the first one and then check which values from the second table match the first table.
My goal is to get all so called 'achievements' and then check which one the user has reached.
achievements
+---------------+-------------+
| achievementID | description |
+---------------+-------------+
| 1 | goal1 |
| 2 | goal2 |
| 3 | goal3 |
+---------------+-------------+
achievement_user
+---------------------+---------------+--------+
| achievementRecordID | achievementID | userID |
+---------------------+---------------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 3 |
| 3 | 4 | 2 |
| 4 | 3 | 1 |
+---------------------+---------------+--------+
My desired result for a query where I check the results for userID = 1 would be something like
+---------------+---------------+--------------+
| achievementID | description | solvedByUser |
+---------------+---------------+--------------+
| 1 | goal1 | true |
| 2 | goal2 | false |
| 3 | goal3 | true |
+---------------+---------------+--------------+
The new column solvedByUser could be basically any datatype (boolean, int, ...).
I just need a list of all available achievements and then see which one the user has reached.
You can left join the achievments table with achievement_user:
select a.*, (au.userID is not null) solvedByUser
from achievements a
left join achievement_user au
on au.achievementID = a.achievementID
and au.userID = 1
solvedByUser is a 0/1 flag that indicates whether the given achievement was reached by the given user.
I think you need a left join -
SELECT A.achievementID, A.description, CASE WHEN AU.userID IS NOT NULL THEN 'true' ELSE 'false' solvedByUser
FROM achievements A
LEFT JOIN achievement_user AU ON A.achievementID = AU.achievementID
AND userID = 1; -- YOUR INPUT ID
You need a left join:
select a.*,
case when u.achievementID is null then 'false' else 'true' end solvedByUser
from achievements a left join achievement_user u
on u.achievementID = a.achievementID and u.userid = 1
Related
I have two tables 'property' and 'bookings'. I want to find out property for city, checkin and checkout when bookings table is empty.
when bookings table empty, for city = 'bali' and checkin = '2020-07-20' and checkout = '2020-07-30', expected output is property id 1 and 2.
when bookings table not empty, for city = 'bali' and checkin = '2020-07-20' and checkout = '2020-07-30', expected output is property id 1.
Query should work both when bookings table is empty / not empty.
property:
+----+---------+------+
| id | city | type |
+----+---------+------+
| 1 | bali | 1 |
| 2 | bali | 1 |
| 3 | bangkok | 1 |
+----+---------+------+
bookings:
+----+-------------+------------+------------+
| id | property_id | checkin | checkout |
+----+-------------+------------+------------+
| 1 | 1 | 2020-07-18 | 2020-07-19 |
| 2 | 2 | 2020-07-20 | 2020-07-25 |
| 3 | 3 | 2020-07-20 | 2020-07-30 |
+----+-------------+------------+------------+
What will be best approach subquery or left join? I tried both approach but unable to get the expected result.
As #Strawberry suggested, I am able to make it work:
SELECT property.id FROM property
LEFT JOIN bookings
ON bookings.checkin < '2020-08-30'
AND bookings.checkout > '2020-08-20'
AND bookings.property_id = property.id
WHERE city = 'bali'
AND bookings.id IS NULL
Try this:
SELECT *
FROM property
LEFT JOIN bookings ON bookings.property_id = property.id
WHERE city='bali'
AND IF(bookings.id IS NULL, 1, bookings.checkin = '2020-07-18')
AND IF(bookings.id IS NULL, 1, bookings.checkout = '2020-07-19')
If there doesn't exists any record in the bookings table then simply apply the filter on the city only otherwise filter will be applied ont eh checkin and checkout as well.
I am trying to fetch a table on certain conditions with join. My table is:
tab_registrations
--------------------------------------------
reg_id |familyid| familyname | parent_id |
| | | |
-------|--------|-------------|-----------|
1 | 2 | null | null |
-------|--------|-------------|-----------|
2 | others | abc | 3 |
-------|--------|-------------|-----------|
3 | 3 | null | null |
-------|--------|-------------|-----------|
4 | others | def | 2 |
-------|--------|-------------|-----------|
tab_family
-------------------------------------
family_id | family_name | parent_id |
| | |
-------------------------------------
1 | tyu | 0 |
-------------------------------------
2 | xyz | 1 |
-------------------------------------
3 | mno | 2 |
-------------------------------------
I want to join these tables on:
if tab_registrations.family not equal to null, then select corresponding parent_id from tab_family
SELECT tab_registration.*,tab_family.family_id,tab_family.parent_id
FROM `tab_registration`
join tab_family on tab_registration.family_id = tab_family.family_id
WHERE reg_id = 1
if tab_registrations.family is equal to 'others', then select tab_registrations.familyname and tab_registrations.parent_id
When I try the above query if tab_registrations.family = 'others', no rows fetched
How can I achieve this? Can anyone help me?
Change to LEFT JOIN with the condition that tab_registration.familyid is not equal to others. Also, you can use conditional CASE..WHEN statements to get the familyname and parent_id values.
SELECT tr.*,
CASE WHEN tr.familyid = 'others' THEN tr.familyname
ELSE tf.family_name
END AS familyname,
CASE WHEN tr.familyid = 'others' THEN tr.parent_id
ELSE tf.parent_id
END AS parent_id
FROM tab_registration tr
LEFT JOIN tab_family tf
ON tr.family_id = tf.family_id AND
tr.familyid <> 'others'
WHERE tr.reg_id = 1
For multi-table queries, it if preferable to use Aliasing for code clarity and readability.
may be useful this query
SELECT tr.*,tf.family_id,tf.parent_id,
IF(tr.familyid='others',tr.familyname,tf.family_name) as fname
IF(tr.familyid='others',tr.parent_id,tf.parent_id) as parentId
FROM `tab_registration` tr
left join tab_family tf on tr.family_id = tf.family_id
Tables
__________________ ________________________________
|______name________| |____________scores______________|
|___id___|__name___| |_id_|_user-id_|_name-id_|_score_|
| 1 | bob | | 1 | 3 | 1 | 5 |
| 2 | susan | | 2 | 1 | 3 | 4 |
| 3 | geoff | | 3 | 3 | 2 | 3 |
| 4 | larry | | 4 | 2 | 4 | 5 |
| 5 | peter | | 5 | 1 | 1 | 0 |
-------------------- ----------------------------------
Im looking to write a query that returns a RANDOM name from the 'name' table, that the user hasnt scored so far.
So given user '1' for example, it could return 'susan, larry or peter' as user '1' hasnt given them a score yet.
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id
WHERE votes.user_id = 1
AND (votes.score IS NULL);
So far I have this, but it doesnt seem to be working as I would like
(atm it doesnt return a random, but all, but this is wrong)
Any help would be appreciated.
If you are filtering on some field of outer joined table type of join is automatically changed to inner. In your case it's condition
votes.user_id = 1
So you need to move that condition from WHERE to ON
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id and votes.user_id = 1
WHERE (votes.score IS NULL);
Consider moving the condition from WHERE to JOIN ON clause since you are performing an OUTER JOIN else the effect would be same as INNER JOIN
LEFT JOIN votes
ON names.id = votes.name_id
AND votes.user_id = 1
WHERE votes.score IS NULL
ORDER BY RAND();
You could apply :
SELECT name FROM name join scores on name.id=scores.user_id WHERE scores.score=0
You can perform this as a sub-query
SELECT *
FROM names
WHERE id NOT IN (SELECT name_id FROM votes WHERE user_id=1)
ORDER BY RAND()
LIMIT 1
I have the following two tables
games
+--------+-------------+----------------+----------------+
| gameID | challengeID | player1_userID | player2_userID |
+--------+-------------+----------------+----------------+
| 1 | 1 | 1 | 2 |
| 2 | 1 | 1 | 3 |
| 3 | 2 | 1 | 4 |
+--------+-------------+----------------+----------------+
challenges
+-------------+---------+
| challengeID | content |
+-------------+---------+
| 1 | one |
| 2 | two |
| 3 | three |
+-------------+---------+
And I want the output to be as below. Basically, a user (say, userID = 1) wants all challenges' data along with the count of the games played against each challenge and also information on whether that challenge has been played by the requesting user.
output (for userID = 1)
+-------------+---------+---------+---------------------+
| challengeID | content | n_games | played_by_requestor |
+-------------+---------+---------+---------------------+
| 1 | one | 2 | TRUE |
| 2 | two | 1 | TRUE |
| 3 | three | 0 | FALSE |
+-------------+---------+---------+---------------------+
This appeared to be deceptively simple, but I've been trying it for the past 4 hours (it is 1:35am now) and the best I could get to was the code below.
SET #myID = 1;
SELECT
COUNT(g1.challengeID) as n_games,
CASE
WHEN g.gameID IS NULL THEN FALSE ELSE TRUE
END AS played_by_requestor,
c.*
FROM challenges c
LEFT JOIN games g
ON c.challengeID = g.challengeID AND
(player1_userID = #myID or player2_userID = #myID)
LEFT JOIN games g1
ON c.challengeID = g1.challengeID
GROUP BY c.challengeID;
But it gives the wrong result. For requestor userID = 1, this code gives n_games = 4 for challengeID = 1 and also n_games = 1 for challengeID = 2.
SQL Fiddle here
Thanks.
Finally, got it to work !
SET #myID = 3;
SELECT
COUNT(g.challengeID) AS n_games,
CASE
WHEN uC.p_challengeID IS NULL THEN FALSE ELSE TRUE
END AS played_by_requestor,
c.*
FROM challenges c
LEFT JOIN games g
ON (c.challengeID = g.challengeID)
LEFT JOIN
(SELECT g1.challengeID AS p_challengeID
FROM games g1
WHERE (player1_userID = #myID OR player2_userID = #myID)
GROUP BY g1.challengeID) uC
ON c.challengeID = uC.p_challengeID
GROUP BY c.challengeID;
If there is a more elegant solution (e.g. without using two joins on games table), I'll gladly accept it as the answer.
I have 4 tables:
secu_content
| id | created | modified |
| 910 | 26/12/1982 | 28/12/1984 |
| 911 | 24/12/1982 | 25/12/1984 |
secu_data
| element_id | field_id | data |
| 1 | 1 | 25/12/1984 |
| 2 | 1 | 26/12/1984 |
| 3 | 1 | 27/12/1984 |
| 4 | 1 | 25/12/1984 |
| 4 | 2 | google.com |
secu_elements
| id | item_id |
| 1 | 891 |
| 2 | 711 |
| 3 | 204 |
| 4 | 911 |
secu_fields
| id | type |
| 1 | date |
| 2 | input |
Table secu_content, contains many articles, where the id is the article id.
The other 3 tables gives additional information and I want to join them.
I want to get results that includes all secu_content rows and all the columns + calc_date + calc_link
calc_date <- the data column from secu_data where field_id=1 (see secu_fields)
calc_link <- the data column from secu_data where field_id=2 (see secu_fields)
The problem is that I get 2 rows where secu_content id=911 (one row with the correct calc_date and second row with the correct calc_link), and I need one row with both.
This is my SQL:
SELECT a.id
, a.created
, a.modified
, fe.item_id AS calc_date_item_id
, fd.data AS calc_date
, CASE WHEN fd.data IS NOT NULL AND ff.type = "date" THEN fd.data
WHEN a.modified = '0000-00-00 00:00:00' THEN a.created ELSE a.modified
END as calc_date
, CASE WHEN fd.data IS NOT NULL AND ff.type = "input" THEN fd.data
END as calc_link
FROM secu_content AS a
LEFT
JOIN secu_fieldsandfilters_elements AS fe
ON fe.item_id = a.id
AND fe.content_type_id=1
LEFT
JOIN secu_fieldsandfilters_data AS fd
ON fd.element_id = fe.id
LEFT
JOIN secu_fieldsandfilters_fields as ff
ON ff.id = fd.field_id
ORDER BY a.id DESC;
Thanks in advance
Israel
Fast and dirty solution is to use second join to secu_data like that (simplified, add logic you need)
SELECT id, d1.data as `calc_date`, d2.data as `calc_link`
FROM secu_content
LEFT JOIN secu_data d1 ON secu_content.id = d1.element_id AND field_id = 1
LEFT JOIN secu_data d2 ON secu_content.id = d2.element_id AND field_id = 2