Problems getting LEFT OUTER JOIN to work - mysql

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)

Related

MySQL Query full table scan when keys available

In attempting to pull a large series of columns (~15-20) from several joined tables, I put together 2 views that would pull the necessary information. In my local DB (only ~1k posts rows), joining these views worked fine, however; when I created those same views on our production DB (~30k posts rows) and attempted to join the view, I realized that that solution wouldn't scale beyond a test dataset.
I attempted to migrate those 2 views (categories data—like categories.title—and creators' data—like users.display_name) into a CTE post_data which, in theory, would act as a keyed version of those views, and allow me to get all post data for the eligible posts.
I have put together a sample DBFiddle with some test data to explain the table structure. The actual data has many more columns, but this is representative of the joins necessary to build the query.
table : posts
+-----+-----------+------------+------------------------------------------+----------------------------------------+
| id | parent_id | created_by | message | attachments |
+-----+-----------+------------+------------------------------------------+----------------------------------------+
| 8 | NULL | 8 | laptop for sale | [{"media_id": 1380}] |
| 9 | NULL | 4 | NEW lamp shade up for grabs | [{"media_id": 1442}, {"link_id": 103}] |
| 10 | 1 | 7 | Oooh I could be interested | |
| 11 | 1 | 7 | DMing you now! I've been looking for one | |
+-----+-----------+------------+------------------------------------------+----------------------------------------+
table : users
+----+------------------+---------------------------+
| id | display_name | created_at |
+----+------------------+---------------------------+
| 1 | John Appleseed | 2018-02-20T00:00:00+00:00 |
| 2 | Massimo Jenkins | 2018-05-14T00:00:00+00:00 |
| 3 | Johanna Marionna | 2018-06-05T00:00:00+00:00 |
| 4 | Jackson Creek | 2018-11-15T00:00:00+00:00 |
| 5 | Joe Schmoe | 2019-01-09T00:00:00+00:00 |
| 6 | John Johnson | 2019-02-14T00:00:00+00:00 |
| 7 | Donna Madison | 2019-05-14T00:00:00+00:00 |
| 8 | Jenna Kaplan | 2019-06-23T00:00:00+00:00 |
+----+------------------+---------------------------+
table : categories
+----+------------+------------+-------------------------------------------------------+
| id | created_by | title | description |
+----+------------+------------+-------------------------------------------------------+
| 1 | 2 | Technology | Anything tech; Consumer, business or education tools! |
| 2 | 2 | Home Goods | Anything for the home |
+----+------------+------------+-------------------------------------------------------+
table : categories_posts
+---------+-------------+
| post_id | category_id |
+---------+-------------+
| 8 | 1 |
| 9 | 1 |
| 10 | 1 |
| 11 | 1 |
+---------+-------------+
table : users_categories
+---------+-------------+
| user_id | category_id |
+---------+-------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
+---------+-------------+
table : posts_removed
+---------+----------------------+------------+
| post_id | removed_at | removed_by |
+---------+----------------------+------------+
| 10 | 2019-01-22 09:08:14 | 7 |
+---------+----------------------+------------+
In the below query, eligible posts are determined in the base SELECT; then, the post_data CTE is joined to the result set (limited to 25 rows) and all columns from the CTE are returned.
WITH post_data AS (
SELECT posts.id,
posts.parent_id,
posts.created_by,
posts.attachments,
categories_posts.category_id,
categories.title,
categories.created_by AS category_created_by,
creator.display_name AS creator_display_name,
creator.created_at AS creator_created_at
/* ... And a whole bunch of other fields from posts, categories_posts, users */
FROM posts
LEFT OUTER JOIN categories_posts
ON categories_posts.post_id = posts.id
LEFT OUTER JOIN categories
ON categories.id = categories_posts.category_id
LEFT OUTER JOIN users creator
ON creator.id = posts.created_by
/* ... And a whole bunch of other joins to facilitate the selected fields */
)
SELECT post_data.*
FROM posts
/* Set up the criteria for the posts selected before getting their data from the CTE */
LEFT OUTER JOIN posts_removed removed ON removed.post_id = posts.id
LEFT OUTER JOIN users user_me ON user_me.id = "1"
LEFT OUTER JOIN users_followed ON users_followed.user_id = posts.created_by
AND users_followed.followed_by = user_me.id
LEFT OUTER JOIN categories_posts ON categories_posts.post_id = posts.id
LEFT OUTER JOIN users_categories ON users_categories.category_id = categories_posts.category_id
LEFT OUTER JOIN posts_removed pp_removed ON pp_removed.post_id = posts.parent_id
/* Join our post_data on the post's ID */
JOIN post_data ON post_data.id = posts.id
WHERE
(
(
users_categories.user_id = user_me.id AND users_categories.left_at IS NULL
) OR categories_posts.category_id IS NULL
) AND (
posts.created_by = user_me.id
OR users_followed.followed_by = user_me.id
OR categories_posts.category_id IS NOT NULL
) AND removed.removed_at IS NULL
AND pp_removed.removed_at IS NULL
AND (post_data.id = posts.id OR post_data.id = posts.parent_id)
ORDER BY posts.id DESC
LIMIT 25
In theory, I thought this would work by selecting the rows based on the base select criteria, then doing an index scan for the CTE based on the Post ID; however, it seems that the query optimizer chooses instead to do a full table scan of the posts table.
The EXPLAIN SELECT gave me this information:
+----+-------------+------------------------+--------+-------------------------------+-------------+---------+---------------------------------------------+--------+----------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | extra |
+----+-------------+------------------------+--------+-------------------------------+-------------+---------+---------------------------------------------+--------+----------+----------------------------------------------------+
| 1 | PRIMARY | posts | ALL | PRIMARY,parent_id,created_by | | | | 33870 | 100 | Using temporary; Using filesort |
| 1 | PRIMARY | removed | eq_ref | PRIMARY | PRIMARY | 8 | posts.id | 1 | 19 | Using where |
| 1 | PRIMARY | user_me | const | PRIMARY | PRIMARY | 8 | const | 1 | 100 | Using where; Using index |
| 1 | PRIMARY | categories_posts | eq_ref | PRIMARY | PRIMARY | 8 | api.posts.id | 1 | 100 | |
| 1 | PRIMARY | categories | eq_ref | PRIMARY | PRIMARY | 8 | categories_posts.category_id | 1 | 100 | Using index |
| 1 | PRIMARY | users_categories | eq_ref | user_id_2,user_id,category_id | user_id_2 | 16 | user_me.id,api.categories_posts.category_id | 1 | 100 | Using where |
| 1 | PRIMARY | users_followed | eq_ref | user_id,followed_by | user_id | 16 | posts.created_by,api.user_me.id | 1 | 100 | Using where; Using index |
| 1 | PRIMARY | pp_removed | eq_ref | PRIMARY | PRIMARY | 8 | api.posts.parent_id | 1 | 19 | Using where |
| 1 | PRIMARY | <derived2> | ALL | | | | | 493911 | 19 | Using where; Using join buffer (Block Nested Loop) |
| 2 | DERIVED | posts | ALL | | | | | 33870 | 100 | Using temporary |
| 2 | DERIVED | categories_posts | eq_ref | PRIMARY | PRIMARY | 8 | api.posts.id | 1 | 100 | |
| 2 | DERIVED | categories | eq_ref | PRIMARY | PRIMARY | 8 | api.categories_posts.category_id | 1 | 100 | |
| 2 | DERIVED | posts_votes | ref | post_id | post_id | 8 | api.posts.id | 1 | 100 | Using index |
| 2 | DERIVED | pp | eq_ref | PRIMARY | PRIMARY | 8 | api.posts.parent_id | 1 | 100 | |
| 2 | DERIVED | pp_removed | eq_ref | PRIMARY | PRIMARY | 8 | api.pp.id | 1 | 100 | Using index |
| 2 | DERIVED | removed | eq_ref | PRIMARY | PRIMARY | 8 | api.posts.id | 1 | 100 | Using index |
| 2 | DERIVED | creator | eq_ref | PRIMARY | PRIMARY | 8 | api.posts.created_by | 1 | 100 | |
| 2 | DERIVED | usernames | ref | user_id | user_id | 8 | api.creator.id | 1 | 100 | |
| 2 | DERIVED | verifications | ALL | | | | | 4 | 100 | Using where; Using join buffer (Block Nested Loop) |
| 2 | DERIVED | categories_identifiers | ref | category_id | category_id | 8 | api.categories.id | 1 | 100 | |
+----+-------------+------------------------+--------+-------------------------------+-------------+---------+---------------------------------------------+--------+----------+----------------------------------------------------+
Beyond this, I tried refactoring my query to try and force key usage in the posts table, such as using FORCE INDEX(PRIMARY) in the select, and moving the CTE be the base query and adding a filter WHERE id IN ({the original base query}), but it seems the optimizer still does a full table scan.
In case it's helpful to decode what's happening in the query plan:
At time of writing, there are 33,387 posts rows, but the query plan shows
The query plan shows a full table scan which returns 33,870 rows
The query plan also shows the derived table (<derived2>) as having 493,911 rows
My core questions are:
Am I correct when I say that subqueries should only be executed once per result row from the base select query? If so, then the CTE should also use the JOIN on posts.id and likely use the table index?
Why does the query plan show that it selects 33,870 rows when there are only 33,387? And where do the 493,911 rows come from?
How do you prevent a full table scan in this case?
Give this a try... Do the LIMIT 25 before JOINing to the WITH:
SELECT * FROM
( SELECT ... FROM posts
JOIN categories_posts ...
ORDER BY posts.id DESC
LIMIT 25 ) AS x
JOIN post_data
ON post_data.id IN (x.id, x.parent_id)
ORDER BY posts.id DESC

Limiting a sql query result with join and conditions

I am writting a simple application that is ordering my medias (pictures, music, videos...). Each media can ben associated with 0 to many tags.
My goal is to have a UI where I can search my medias (for exemple, show images and videos tagged like %hol%, and return both holidays tagged photos and hollywood tagged photos).
Here's my database :
Table medias
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| path | varchar(400) | NO | UNI | NULL | |
| type | varchar(5) | NO | | NULL | |
| libelle | varchar(200) | NO | | NULL | |
| ratings | int(2) | NO | | NULL | |
+---------+--------------+------+-----+---------+----------------+
Table tags
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| libelle | varchar(200) | NO | UNI | NULL | |
+---------+--------------+------+-----+---------+----------------+
Table medias_tags
+----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| id_media | int(11) | NO | PRI | NULL | |
| id_tag | int(11) | NO | PRI | NULL | |
+----------+---------+------+-----+---------+-------+
As I have many medias, I had to limit the result. So in my front-end, I made a pagination system, and query my medias according to the page I am (for exemple, if I am on page 3, I put LIMIT 20 OFFSET 60 in my sql statement).
Now I'm trying to filter my medias. I have a searchbar, and if I type 'hol', I want to get 20 medias with tagged like '%hol%' (holidays, hollywood...)
Getting filtered medias works, but I don't know how to get exactly 20 medias.
Here's my sql query without filtering:
SELECT
medias.id, medias.path, medias.type, medias.libelle as libelle, medias.ratings, tags.libelle as tag
FROM (select * from medias LIMIT ? OFFSET ?) medias
left outer join medias_tags on medias.id = medias_tags.id_media
left outer join tags on tags.id = medias_tags.id_tag
And here's my filtering sql query:
SELECT
medias.id, medias.path, medias.type, medias.libelle as libelle, medias.ratings, tags.libelle as tag
FROM medias
left outer join medias_tags on medias.id = medias_tags.id_media
left outer join tags on tags.id = medias_tags.id_tag
WHERE tags.libelle LIKE ? [OR tags.libelle LIKE ? ...]
(last parameters are my tags)
Both query work well, but I can't find a way to limit my filtered result. Here's a sample of my filtering query result :
+----+-------------+-------+-------------------+---------+------------+
| id | path | type | libelle | ratings | tag |
+----+-------------+-------+-------------------+---------+------------+
| 11 | mock/02.jpg | PHOTO | 02.jpg | 0 | dark |
| 1 | mock/03.jpg | PHOTO | Purple | 5 | wallpapper |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wave |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wallpapper |
+----+-------------+-------+-------------------+---------+------------+
How can I limit my filtering result to only return n different medias id ? Is there a pure sql solution ? Maybe with stored procedures ?
Thanks !
EDIT :
Here's a result I'd like with limit = 7 :
+----+-------------+-------+-------------------+---------+------------+
| id | path | type | libelle | ratings | tag |
+----+-------------+-------+-------------------+---------+------------+
| 11 | mock/02.jpg | PHOTO | 02.jpg | 0 | dark |
| 7 | mock/01.jpg | PHOTO | NEWLY ADDED MEDIA | 8 | wallpapper |
| 2 | mock/02.jpg | PHOTO | Night | 5 | wallpapper |
| 2 | mock/02.jpg | PHOTO | Night | 5 | dark |
| 1 | mock/03.jpg | PHOTO | Purple | 5 | wallpapper |
| 4 | mock/03.jpg | PHOTO | Purple 2 | 5 | wallpapper |
| 5 | mock/03.jpg | PHOTO | Purple 3 EDITED | 8 | wallpapper |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wave |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wallpapper |
+----+-------------+-------+-------------------+---------+------------+
I have 9 rows, but only 7 distincts media id. Every media has a tag like '%a%'.
EDIT 2 : someone posted an answer, but deleted it. His idea was to concatenate tags, which would be a nice solution too.
Something like that :
+----+-------------+-------+-------------------+---------+------------+
| id | path | type | libelle | ratings | tag |
+----+-------------+-------+-------------------+---------+------------+
| 11 | mock/02.jpg | PHOTO | 02.jpg | 0 | dark |
| 7 | mock/01.jpg | PHOTO | NEWLY ADDED MEDIA | 8 | wallpapper |
| 2 | mock/02.jpg | PHOTO | Night | 5 | wallpapper, dark |
| 1 | mock/03.jpg | PHOTO | Purple | 5 | wallpapper |
| 4 | mock/03.jpg | PHOTO | Purple 2 | 5 | wallpapper |
| 5 | mock/03.jpg | PHOTO | Purple 3 EDITED | 8 | wallpapper |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wave, wallpapper |
+----+-------------+-------+-------------------+---------+------------+
But I have no idea how to write this sql query...
Use GROUP_CONCAT in order to build a tag string per media and outer join this result. Then apply your limit clause as desired
select
medias.id,
medias.path,
medias.type,
medias.libelle,
medias.ratings,
mtags.tags
from medias
left outer join
(
select id_media, group_concat(tags.libelle order by tags.libelle) as tags
from medias_tags
join tags on tags.id = medias_tags.id_tag
group by id_media
) mtags on mtags.id_media = medias.id
order by medias.id
limit 20 offset 60;
Are you expecting like this?
SELECT
medias.id, medias.path, medias.type, medias.libelle as libelle, medias.ratings, tags.libelle as tag
FROM medias
left outer join medias_tags on medias.id = medias_tags.id_media
left outer join tags on tags.id = medias_tags.id_tag
WHERE tags.libelle LIKE ? [OR tags.libelle LIKE ? ...]
order by medias.id
limit 0,10
Here limit is used for first 10 record. you can use stored procedure for passing the two params of limit and pick the filtered result

Counting multiple value ocurrences on multiple columns in a single query

I have a search section for looking up products which has a navigation bar for filtering purposes that shows the total results of each product feature. For example:
TOTAL RESULTS 60
New (32)
Used (28)
Particular (10)
Company (50)
In mysql I have the following queries (one per feature)
Type
SELECT a.id_type, whois.name as whoisName, COUNT(a.id_type) as countWhois
FROM (published a
INNER JOIN types whois ON whois.id = a.id_type)
GROUP BY id_type
+---------+------------+------------+
| id_type | whoisName | countWhois |
+---------+------------+------------+
| 0 | Company | 50 |
| 1 | Particular | 10 |
+---------+------------+------------+
Condition
SELECT a.id_condition, cond.name as condName, COUNT(a.id_condition) as countCondition
FROM (published a
INNER JOIN conditions cond ON cond.id = a.id_condition)
GROUP BY id_condition
+--------------+---------------+----------------+
| id_condition | conditionName | countCondition |
+--------------+---------------+----------------+
| 0 | New | 32 |
| 1 | Used | 28 |
+--------------+---------------+----------------+
I want to summarize the two queries in a single one but can´t figure out how. I was thinking something like this:
+---------+------------+------------+--------------+---------------+----------------+
| id_type | whoisName | countWhois | id_condition | conditionName | countCondition |
+---------+------------+------------+--------------+---------------+----------------+
| 0 | Company | 50 | NULL | NULL | NULL |
| 1 | Particular | 10 | NULL | NULL | NULL |
| NULL | NULL | NULL | 0 | New | 32 |
| NULL | NULL | NULL | 1 | Used | 28 |
+---------+------------+------------+--------------+---------------+----------------+
Is this possible?
Thanks and sorry if my English is bad, it's not my native language.

Removing duplicate rows if value from 1 row exists in another row SQL

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

Issue with my right outer join in MYSQL

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)