Why does this query return nothing? - sql

This query works fine but I'm having trouble trying to figure out why it does not return anything if the user does not have any categories in the table "user_categories"? This is the table structure:
users: user id, username, user city
categories: category id, category name
user_categories: user id, category id
SELECT users.*, GROUP_CONCAT(categories.category_name) AS categories
FROM users
INNER JOIN user_categories ON users.user_id = user_categories.user_id
INNER JOIN categories ON user_categories.category_id = categories.category_id
WHERE users.user_city = 'brooklyn'
GROUP BY users.user_id
LIMIT 10
I just need for the new column "categories" to be empty if no rows existed for the user_id in user_categories...
Thanks!

You are using the wrong type of join - inner join will only succeed if a match is found in both tables. Instead, you want to try an outer join. Try something like this:
SELECT users.*, GROUP_CONCAT(categories.category_name) AS categories
FROM users
LEFT OUTER JOIN user_categories ON users.user_id = user_categories.user_id
LEFT OUTER JOIN categories ON user_categories.category_id = categories.category_id
WHERE users.user_city = 'brooklyn'
GROUP BY users.user_id
LIMIT 10
The Wikipedia SQL JOIN article is a pretty decent summary of the join types available.

SELECT users.*, GROUP_CONCAT(categories.category_name) AS categories
FROM users
LEFT JOIN
user_categories ON users.user_id = user_categories.user_id
LEFT JOIN
categories ON user_categories.category_id = categories.category_id
WHERE users.user_city = 'brooklyn'
GROUP BY
users.user_id
LIMIT 10
Note that on a MyISAM table, a subquery solution will probably be more efficient:
SELECT users.*,
(
SELECT GROUP_CONCAT(category_name)
FROM user_categories uc
JOIN categories c
ON c.category_id = uc.category_id
WHERE uc.user_id = users.id
) AS categories
FROM users
WHERE users.user_city = 'brooklyn'
ORDER BY
user_id
LIMIT 10
See this article in my blog for more detail:
Aggregates: subqueries vs. GROUP BY

An inner join will not return a record if there is not a record in both tables. You may be wanting a left outer join.

That's because you use an INNER JOIN to user_categories. If you use a LEFT JOIN, null data will be returned for that table if there is no corresponding ID for the user.

Related

How to replace exist in Hive with two correlated subqueries

I have a query that looks like this
SELECT u.id, COUNT(*)
FROM users u, posts p
WHERE u.id = p.owneruserid
AND EXISTS (SELECT COUNT(*) as num
FROM postlinks pl
WHERE pl.postid = p.id
GROUP BY pl.id
HAVING num > 1) --correlated subquery 1
AND EXISTS (SELECT *
FROM comments c
WHERE c.postid = p.id); --correlated subquery 2
GROUP BY u.id
I researched and read that in Hive IN or EXIST are not supported statements. I read that a workaround for this would be to use a LEFT JOIN. I have tried this but I am having trouble with the GROUP BY u.id. I read that this needs to be paired always with an aggregation function like COUNT() however I'm not sure how I can rewrite this query so as to get it to work. All the other examples I have seen online do not seem to be as complicated as this one.
Like you said, you can convert them to left join or may be left join since they uses exists in both subquery. Simply convert your subqueries to inline view and join them with original tables.
SELECT u.id, COUNT(*)
FROM users u
inner join posts p on u.id = p.owneruserid
left outer join (SELECT COUNT(*) as num, pl.postid postid
FROM postlinks pl
GROUP BY pl.postid
HAVING num > 1) pl ON pl.postid = p.id --correlated subquery 1 with left join
left outer join (SELECT postid FROM comments c GROUP BY postid)c ON c.postid = p.id --correlated subquery 2 with left join
WHERE ( c.postid is not null AND pl.postid is not null) -- this ensure data exists in both subquery
GROUP BY u.id
With left join, there may be chance of duplicates, you can use group by in subqry2 to avoid it.

Query to find the max of a value with multiple group by attributes

I am trying to get the max bidamt on a particular item and display the user who has bid that amount
I am able to get the results in two different queries each returning a subset of what I want
select username, auctionbids.itemid, description, bidamt from AuctionBids
inner join users on auctionbids.userid = users.id
inner join auctionitems on auctionbids.itemid = auctionitems.itemid
select ab.itemid,max(ab.bidamt) as bidmax from auctionbids as ab group by ab.itemid
I want to get username, itemid, item descripton, max(bidamt) joining three tables - users, auctionitems, auctionbids.
users contains userid and username
auctionitems contains itemid and item description
auctionbids contains userid, itemid, bidamt
The following query should do what you want:
SELECT a.item_description,a.username,a.bidamt FROM
(
SELECT au.item_description,u.username,ab.bidamt, RANK() OVER(PARTITION BY au.itemid ORDER BY ISNULL(ab.bidamt,0) DESC) AS [Rank]
FROM auctionitems au
LEFT JOIN auctionbids ab ON au.itemid = ab.itemid
LEFT JOIN users u ON ab.userid = u.userid ) a WHERE [Rank] = 1
As you said, the two different queries each returning a subset of what you want, your query should like this:
SELECT am.username, am.itemid, am.descripton, max(am.bidamt) AS bidmax
FROM (
SELECT username, auctionbids.itemid, auctionitems.description, bidamt FROM AuctionBids
INNER JOIN users ON auctionbids.userid = users.id
INNER JOIN auctionitems ON auctionbids.itemid = auctionitems.itemid
) AS am
GROUP BY am.itemid
Without example data, it is impossible to verify this works correctly. But you will need to match the bidamt with the MAX bidamt for a particular item.
SELECT DISTINCT
users.username,
auctionbids.itemid,
auctionitems.description,
auctionbids.bidamt
FROM
users
INNER JOIN auctionbids on users.userid = auctionbids.userid
INNER JOIN auctionitems on auctionbids.itemid = auctionitems.itemid
WHERE
auctionbids.bidamt = (SELECT MAX(bidamt) OVER (PARTITION BY itemid, description))
AND
itemid = < whatever you want >
Thanks for the ideas which led to this query -
SELECT am.*, auctionbids.submitted, users.username, auctionitems.description
FROM
(SELECT ab.itemid, max(ab.bidamt) as bidmax
FROM
auctionbids ab
GROUP BY ab.itemid) AS am
INNER JOIN auctionbids ON am.itemid = auctionbids.itemid and am.bidmax = auctionbids.bidamt
INNER JOIN users ON auctionbids.userid = users.id
INNER JOIN auctionitems ON auctionbids.itemid = auctionitems.itemid
So basically the max has to be found with a group by on itemid and then the inner join with rest of the tables to fetch the attributes.

sql, sqlite SELECT with inner join

I'm wondering how to select one column twice using an inner joinor some other way. my database is sqlite and i use PDO db driver.
My Example:
SELECT
orders.id,
orders.order_number,
clients.first_name,
clients.last_name,
users.name AS user_name
FROM orders
INNER JOIN clients ON
orders.client_id = clients.id
INNER JOIN users ON
orders.created_by = users.id
I want to get also, the user_name who edited this record
orders.edited_by = users.id
How to join this selection?
You'll need to use table aliases.
SELECT
orders.id,
orders.order_number,
clients.first_name,
clients.last_name,
creator.name AS creator_user_name
editor.name AS editor_user_name
FROM orders
INNER JOIN clients ON
orders.client_id = clients.id
INNER JOIN users creator ON
orders.created_by = creator.id
INNER JOIN users editor ON
orders.edited_by = editor.id
Use aliases in your table names, so you can use multiple references to the same table. This also can help make large queries easier to read.
SELECT
orders.id,
orders.order_number,
clients.first_name,
clients.last_name,
createUsers.name AS creator_name,
editUsers.name AS editor_name
FROM orders
INNER JOIN clients ON
orders.client_id = clients.id
INNER JOIN users As createUsers ON
orders.created_by = users.id
INNER JOIN users As editUsers ON
orders.edited_by = users.id
You can use as many "instances" of the same table as you wish.

Help with MySQL Query?

I have two tables rooms and users. I want to get only rooms.room_id, users.user_name with user_id = 1. I can get the result of all users with following sql...
select rooms.room_id,
rooms.user_id,
users.user_name
from rooms
LEFT JOIN users ON rooms.user_id = users.user_id
When I do like this to filter the result with user_id = 1 ... I got error.
select rooms.room_id,
rooms.user_id,
users.user_name
from rooms where rooms.user_id = 1
LEFT JOIN users ON rooms.user_id = users.user_id
What should I do?
ANSI-92 JOIN syntax (when you see LEFT JOIN ...) dictates that the WHERE clause comes after the JOIN(s):
SELECT r.room_id,
r.user_id,
u.user_name
FROM ROOMS r
LEFT JOIN users ON u.user_id = r.user_id
WHERE r.user_id = 1
You were close.
Write the query as:
select rooms.room_id,
rooms.user_id,
users.user_name
from rooms
LEFT JOIN users ON rooms.user_id = users.user_id
WHERE roows.user_id = 1
Try:
select rooms.room_id,
rooms.user_id,
users.user_name
from rooms
LEFT JOIN users ON rooms.user_id = users.user_id
WHERE rooms.user_id = 1
The syntax of a simple SQL SELECT query is:
SELECT [a list of fields]
FROM [a single table name maybe with an alias, or a join of tables]
WHERE [a filter, applied over some fields of the tables in the FROM clause]
You could read an introductory tutorial here.
It helps to state what error you get.
I would guess that the problem is that the where clause needs to be after the joins
select rooms.room_id,
rooms.user_id,
users.user_name
from rooms
LEFT JOIN users ON rooms.user_id = users.user_id
where rooms.user_id = 1

What kind of SQL join would this be?

I need to go to two tables to get the appropriate info
exp_member_groups
-group_id
-group_title
exp_members
-member_id
-group_id
I have the appropriate member_id
So I need to check the members table, get the group_id, then go to the groups table and match up the group_id and get the group_title from that.
INNER JOIN:
SELECT exp_member_groups.group_title
FROM exp_members
INNER JOIN exp_member_groups ON exp_members.group_id = exp_member_groups.group_id
WHERE exp_members.member_id = #memberId
SELECT g.group_title
FROM exp_members m
JOIN exp_member_groups g ON m.group_id = g.group_id
WHERE m.member_id = #YourMemberId
If there is always a matching group, or you only want rows where it is, then it would be an INNER JOIN:
SELECT g.group_title
FROM exp_members m
INNER JOIN
exp_member_groups g
ON m.group_id = g.group_id
WHERE m.member_id = #member_id
If you want rows even where group_id doesn't match, then it is a LEFT JOIN - replace INNER JOIN with LEFT JOIN in the above.