Outer joins don't seem to work whether I use a left outer join or right outer join:
SELECT * FROM `event_orchestra_musicians` eom
right outer join `orchestra_instruments` oi on eom.instrument_id = oi.oi_id
where event_id = 2
order by instrument_id
Orchestra_instruments contains different instruments with a unique ID i.e.:
Violin
Viola
Harp
Piccolo
Event_Orchestra_Musicians is a look up table to join musicians to an instrument i.e.:
musician_id, instrument_id, event_id,
1 1 2
2 1 2
3 3 2
4 2 2
When I do any outer join, using the data in those tables, I would get the results of an inner join (Piccolo wouldn't show up with a null musician_id, it wont show up at all). Is there something I'm doing wrong?
EDIT:
So I did some monkeying around. The issue seems to be because there is a record in the events_orchestra_musicians table with an event_id of 5 and an instrument_id of 7. If I remove that record, then the outer join works. What I don't get is if that record is there and I use the where clause to look for event_id = 2, why does it matter if there's a record in there with an instrument_id of 7 if its event_id is 5?
Try this:
select oi.oi_id, oi.instrument_name, eom.musician_id
from orchestra_instruments oi
left join event_orchestra_musicians eom on oi.oi_id = eom.instrument_id
where (eom.event_id = 2 or eom.event_id is null)
order by oi.oi_id
You have to use orchestra_instruments as the base table, since that is the one you want all of the records for, even if no musician exists. I can't imagine any reasons for using a Right join over a Left join, and Outer is implied. Also, you have to allow event_id to either be 2 or null, because it cannot be 2 if there is no matching record to join.
You don't have any records in Event_Orchestra_Musicians where instrument_id=4 so you'll need to check for a null value from eom when doing a right outer join.
SELECT * FROM `event_orchestra_musicians` eom
right outer join `orchestra_instruments` oi on eom.instrument_id = oi.oi_id
where event_id = 2 or eom.instrument_id is null
order by instrument_id
Here is my complete setup. Maybe there something with your structure?
mysql> show columns from orchestra_instruments;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| oi_id | int(11) | YES | | NULL | |
| oi_name | varchar(10) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
mysql> show columns from event_orchestra_musicians;
+---------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------+------+-----+---------+-------+
| musician_id | int(11) | YES | | NULL | |
| instrument_id | int(11) | YES | | NULL | |
| event_id | int(11) | YES | | NULL | |
+---------------+---------+------+-----+---------+-------+
3 rows in set (0.01 sec)
mysql> select * from orchestra_instruments;
+-------+---------+
| oi_id | oi_name |
+-------+---------+
| 1 | Violin |
| 2 | Viola |
| 3 | Harp |
| 4 | Piccolo |
+-------+---------+
4 rows in set (0.00 sec)
mysql> select * from event_orchestra_musicians;
+-------------+---------------+----------+
| musician_id | instrument_id | event_id |
+-------------+---------------+----------+
| 1 | 1 | 2 |
| 2 | 1 | 2 |
| 3 | 3 | 2 |
| 4 | 2 | 2 |
+-------------+---------------+----------+
4 rows in set (0.00 sec)
select oi.oi_id, oi.oi_name, eom.musician_id
from orchestra_instruments oi
left join event_orchestra_musicians eom on oi.oi_id = eom.instrument_id
where (eom.event_id = 2 or eom.event_id is null)
order by oi.oi_id;
+-------+---------+-------------+
| oi_id | oi_name | musician_id |
+-------+---------+-------------+
| 1 | Violin | 1 |
| 1 | Violin | 2 |
| 2 | Viola | 4 |
| 3 | Harp | 3 |
| 4 | Piccolo | NULL |
+-------+---------+-------------+
5 rows in set (0.00 sec)
Related
I have a table of products ids and keywords that looks like the following:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| product_id | int(10) unsigned | YES | MUL | NULL | |
| keyword | varchar(255) | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
This table simply stores product ids, and keywords associated with those products. So for example, it might contain:
+----+------------+---------+
| id | product_id | name |
+----+------------+---------+
| 1 | 1 | soft |
| 2 | 1 | red |
| 3 | 1 | leather |
| 4 | 2 | cloth |
| 5 | 2 | red |
| 6 | 2 | new |
| 7 | 3 | soft |
| 8 | 3 | red |
| 9 | 4 | blue |
+----+------------+---------+
In other words:
product 1 is soft, red, and leather.
product 2 is cloth, red and new.
Product 3 is red and soft,
product 4 is blue.
I need some way to take in a product ID, and get back a sorted list of product ids ranked by the number of common keywords
So for example, if I pass in product_id 1, I'd expect to get back:
+----+-------+------------+
| product_id | matches |
+------------+------------+
| 3 | 2 | (product 3 has two common keywords with product 1)
| 2 | 1 | (product 2 has one common keyword with product 1)
| 4 | 0 | (product 4 has no common keywords with product 1)
+------------+------------+
One option uses a self right outer join with conditional aggregation to count the number of matched names between, e.g. product ID 1, and all other product IDs:
SELECT t2.product_id,
SUM(CASE WHEN t1.name IS NOT NULL THEN 1 ELSE 0 END) AS matches
FROM yourTable t1
RIGHT JOIN yourTable t2
ON t1.name = t2.name AND
t1.product_id = 1
WHERE t2.product_id <> 1
GROUP BY t2.product_id
ORDER BY t2.product_id
Follow the link below for a running demo:
SQLFiddle
You need to use an outer join against the keywords for productid 1:
select y.productid, count(y2.keyword)
from yourtable y
left join (
select keyword from yourtable y2 where y2.productid = 1
) y2 on y.keyword = y2.keyword
where y.productid <> 1
group by y.productid
order by 2 desc
SQL Fiddle Demo
Results:
| productid | count(y2.keyword) |
|-----------|-------------------|
| 3 | 2 |
| 2 | 1 |
| 4 | 0 |
I'm having an issue removing all the rows that have a certain value in them and then removing the other rows that have the same value as an already removed rows column.
Here is an example of what I have right now:
SELECT Race.intRaceID, Register.intRegID, Member.intMemberID
FROM Race
LEFT JOIN Register ON Race.intRaceID=Register.intRaceID
LEFT JOIN Member ON Register.intMemberID=Member.intMemberID
which gives me:
+------------+-----------+-------------+
| intRaceID | intRegID | intMemberID |
+------------+-----------+-------------+
| 100 | 10 | 1 |
| 100 | 40 | 2 |
| 200 | NULL | NULL |
| 300 | 30 | 2 |
| 400 | 20 | 4 |
| 500 | NULL | NULL |
+------------+-----------+-------------+
So, what I'm attempting to do is remove a particular intMemberID (keeping the NULLs) and all of the intRaceID's they're associated with.
I added
WHERE Member.intMemberID <> 2 OR Member.intMemberID IS NULL
Giving the result:
+------------+-----------+-------------+
| intRaceID | intRegID | intMemberID |
+------------+-----------+-------------+
| 100 | 10 | 1 |
| 200 | NULL | NULL |
| 400 | 20 | 4 |
| 500 | NULL | NULL |
+------------+-----------+-------------+
but that will not remove all intRaceIDs associated with the intMemberID.
Any help would be greatly appreciated
The table I'm trying to show is this:
+------------+-----------+-------------+
| intRaceID | intRegID | intMemberID |
+------------+-----------+-------------+
| 200 | NULL | NULL |
| 400 | 20 | 4 |
| 500 | NULL | NULL |
+------------+-----------+-------------+
I think you have to write the WHERE clause like in the following query:
SELECT Race.intRaceID, Register.intRegID, Member.intMemberID
FROM Race
LEFT JOIN Register ON Race.intRaceID = Register.intRaceID
LEFT JOIN Member ON Register.intMemberID = Member.intMemberID
WHERE Race.intRaceID NOT IN (
SELECT Race.intRaceID
FROM Race
INNER JOIN Register ON Race.intRaceID = Register.intRaceID
INNER JOIN Member ON Register.intMemberID = Member.intMemberID
WHERE Member.intMemberID = 2)
ORDER BY intRaceID;
This way you exclude all records for which intRaceID is related to intMemberID with a value of 2.
Demo here
I have a two tables :
mysql> select * from quizquestionbank;
| ID | QuestionFilePath | CorrectAnswer |
| 1 | p.wav | 1 |
| 2 | q.wav | 2 |
| 3 | a.wav | 3 |
| 4 | b.wav | 1 |
| 5 | m.wav | 3 |
Second table is :
mysql> select * from quizuserdetails;
| ID | MSISDN | QuestionIdDetails | AnswerRecord |
| 1 | 235346 | 1,3,4,5 | S,F,S,F |
| 2 | 564574 | 4,5,67,88 | F,S,F,s |
| 3 | 500574 | 5,55,66,44,2 | F,F,F,F |
I want to get the IDs from table 1 which are not there in QuestionIdDetails column of second table.
I tried query
Select ID from quizquestionbank where ID not in (Select QuestionIdDetails from quizuserdetails where msisdn = '235346 ');
But this does not work
CAn anybody suggest a way to do it
Use find_in_set() to match the id to the list, but that's not all:
Select disting qb.ID
from quizquestionbank qb
left join quizuserdetails qd
on find_in_set(qb.id, QuestionIdDetails) > 0
and msisdn = '235346'
where qd.id is null
There's 3 key things going on here:
using a left join and including the extra condition in the join condition
the use of find_in_set(), which finds a value in a CSV string, to make the join
using a where clause that filters out matches, leaving only the missed joins
I have a table containing perfectly defined items and a second table with potentially vague/greedy orders as NULL would require all available values for this parameter.
items
+-----------------------+
| item_id | color | size |
|---------+-------+------|
| 1 | blue | 8 |
| 2 | red | 6 |
| 3 | green | 7 |
| 4 | black | 6 |
+------------------------+
orders
+-------------------------+
| order_id | color | size |
|----------+-------+------|
| 1 | red | 6 |
| 2 | green | 8 |
| 3 | NULL | 6 |
| 4 | blue | NULL |
| 5 | NULL | NULL |
+-------------------------+
Is there an efficient way to generate a complete list of items needed to fill all orders?
+--------------------+
| order_id | item_id |
|----------+---------|
| 1 | 2 |
| 3 | 2 |
| 3 | 4 |
| 4 | 1 |
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 5 | 4 |
+--------------------+
It seems to me like an INNER JOIN should be able to do this, but something like this obviously doesn't consider the possibility of NULL values as greedy wildcards in the orders table:
SELECT order_id, item_id
FROM orders
INNER JOIN items ON orders.color = items.color AND orders.size = items.size
Any ideas?
Try the following:
SELECT order_id, item_id
FROM orders
INNER JOIN items ON (orders.color IS NULL OR orders.color = items.color)
AND (orders.size IS NULL OR orders.size = items.size)
Let me know if that helps, or if I misunderstood the question.
If you rewrite the conditions for the JOIN you can get the desired result:
SELECT order_id, item_id
FROM orders
JOIN items
ON ((orders.color = items.color OR orders.color IS NULL)
AND (orders.size = items.size OR orders.size IS NULL))
However, the orders table should probably look more like the result of this query than the current orders table.
You could use the IFNULL function for this:
SELECT order_id, item_id
FROM orders
JOIN items ON IFNULL(orders.color, items.color) = items.color
AND IFNULL(orders.size, items.size) = items.size
If the value in orders is null, then it'll use the value from items (and thus match).
I thought I understood how left outer joins work, but I have a situation that is not working, and I'm not 100% sure if the way I have my query structured is incorrect, or if it's a data issue.
For background, I have the following MySQL table structures:
mysql> describe achievement;
+-------------+----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------------------+------+-----+---------+-------+
| id | varchar(64) | NO | PRI | NULL | |
| game_id | varchar(10) | NO | PRI | NULL | |
| name | varchar(64) | NO | | NULL | |
| description | varchar(255) | NO | | NULL | |
| image_url | varchar(255) | NO | | NULL | |
| gamerscore | smallint(5) unsigned | NO | | 0 | |
| hidden | tinyint(1) | NO | | 0 | |
| base_hidden | tinyint(1) | NO | | 0 | |
+-------------+----------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
and
mysql> describe gamer_achievement;
+----------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------------------+------+-----+---------+-------+
| game_id | varchar(10) | NO | PRI | NULL | |
| achievement_id | varchar(64) | NO | PRI | NULL | |
| gamer_id | varchar(36) | NO | PRI | NULL | |
| earned_epoch | bigint(20) unsigned | NO | | 0 | |
| offline | tinyint(1) | NO | | 0 | |
+----------------+---------------------+------+-----+---------+-------+
5 rows in set (0.00 sec)
As for the data, this is what I have populated here (only pertinent columns included for brevity):
+----+------------+------------------------------+
| id | game_id | name |
+----+------------+------------------------------+
| 1 | 1480656849 | Cluster Buster |
| 2 | 1480656849 | Star Gazer |
| 3 | 1480656849 | Flower Child |
| 4 | 1480656849 | Oyster-meister |
| 5 | 1480656849 | Big Cheese of the South Seas |
| 6 | 1480656849 | Hexic Addict |
| 7 | 1480656849 | Collapse Master |
| 8 | 1480656849 | Survivalist |
| 9 | 1480656849 | Tick-Tock Doc |
| 10 | 1480656849 | Marathon Mogul |
| 11 | 1480656849 | Millionaire Extraordinaire |
| 12 | 1480656849 | Grand Pearl Pooh-Bah |
+----+------------+------------------------------+
12 rows in set (0.00 sec)
and
+----------------+------------+--------------+---------+
| achievement_id | game_id | earned_epoch | offline |
+----------------+------------+--------------+---------+
| 1 | 1480656849 | 0 | 1 |
| 2 | 1480656849 | 0 | 1 |
| 3 | 1480656849 | 0 | 1 |
| 4 | 1480656849 | 1149789371 | 0 |
| 7 | 1480656849 | 1149800406 | 0 |
| 8 | 1480656849 | 0 | 1 |
| 9 | 1480656849 | 1149794790 | 0 |
| 10 | 1480656849 | 1149792417 | 0 |
+----------------+------------+--------------+---------+
8 rows in set (0.02 sec)
In this particular case, the achievement table is the "master" table and will contain the information that I always want to see. The gamer_achievement table only contains information for achievements that are actually earned. For any particular game for any particular gamer, there can be any number of rows in the gamer_achievement table - including none if no achievements have been earned for that game. For example, in the sample data above, achievements with ids 5, 6, 11, and 12 have not been earned.
What I currently have written is
select a.id,
a.name,
ga.earned_epoch,
ga.offline
from achievement a
LEFT OUTER JOIN gamer_achievement ga
ON (a.id = ga.achievement_id and a.game_id = ga.game_id)
where ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024'
and a.game_id = '1480656849'
order by convert (a.id, unsigned)
but this is only returning the full information for those achievements that have actually been earned - the unearned achievement information from the right side table (gamer_achievement) is not being show with the NULL values as I would expect from this type of query. This is what I am expecting to see:
+----+-------------------------------+--------------+---------+
| id | name | earned_epoch | offline |
+----+-------------------------------+--------------+---------+
| 1 | Cluster Buster | 0 | 1 |
| 2 | Star Gazer | 0 | 1 |
| 3 | Flower Child | 0 | 1 |
| 4 | Oyster-meister | 1149789371 | 0 |
| 5 | Big Cheese of the South Seas | NULL | NULL |
| 6 | Hexic Addict | NULL | NULL |
| 7 | Collapse Master | 1149800406 | 0 |
| 8 | Survivalist | 0 | 1 |
| 9 | Tick-Tock Doc | 1149794790 | 0 |
| 10 | Marathon Mogul | 1149792417 | 0 |
| 11 | Millionaire Extraordinaire | NULL | NULL |
| 12 | Grand Pearl Pooh-Bah | NULL | NULL |
+----+-------------------------------+--------------+---------+
12 rows in set (0.00 sec)
What am I missing here? From what I understand, the basic query LOOKS right to me, but I'm obviously missing some piece of critical information.
Many have answered, but I'll try too and hopefully lend in some more clarification. How I have always interpreted it (and you can check so many other posts I've responded to with LEFT joins), I try to list the table I want everything from first (left side... hence read from left to right). Then left join to the "Other" table (right side) on whatever the criteria is between them... Then, when doing a left join, and there are additional criteria against the right side table, those conditions would stay with that join condition. By bringing them into the "WHERE" clause would imply an INNER JOIN (must always match) which is not what you want... I also try to always show the left table alias.field = right table alias.field to keep the correlation clear... Then, apply the where clause to the basis criteria you want from the first table.. something like
select
a.id,
a.name,
ga.earned_epoch,
ga.offline
from
achievement a
LEFT OUTER JOIN gamer_achievement ga
ON a.id = ga.achievement_id
AND a.game_id = ga.game_id
AND ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024'
where
a.game_id = '1480656849'
order by
convert (a.id, unsigned)
Notice the direct relation between "a" and "ga" by the common ID and game ID values, but then tacked on the specific gamer. The where clause only cares at the outer level of achievement based on the specific game.
In the WHERE clause you discard some rows that the LEFT JOIN would have filled with NULL values. You want to put the condition ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024' inside the JOIN clause.
Another option is:
LEFT OUTER JOIN (SELECT * FROM gamer_achievement
WHERE ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024'
) ga
Remember that the join is performed, and at this time, NULL values come if the condition cannot be met; then the where filter applies.
WHERE clauses filter results from the entire result set. If you want to apply a filter only to the JOIN, then you can add the expression to the ON clause.
In the following query, I've moved the filter expression that applies to the joined table (ga.gamer_id =) from the WHERE clause to the ON clause. This prevents the expression from filtering out rows where gamer_achievement values are NULL.
SELECT a.id,
a.name,
ga.earned_epoch,
ga.offline
FROM achievement a
LEFT OUTER JOIN gamer_achievement ga
ON ga.achievement_id = a.id
AND ga.game_id = a.game_id
AND ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024'
WHERE
a.game_id = '1480656849'
ORDER BY CONVERT(a.id, UNSIGNED)
It's because of this line:
where ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024'
If the gamer hasn't earned the achievement, the ga.gamer_id value will be NULL and not qualify for the WHERE condition.
My guess is that the where clause is filtering out your desired results, moving it to the left join may work.
select a.id,
a.name,
ga.earned_epoch,
ga.offline
from achievement a
LEFT OUTER JOIN gamer_achievement ga
ON (a.id = ga.achievement_id and
a.game_id = ga.game_id and
ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024' and
a.game_id = '1480656849')
order by convert (a.id, unsigned)