Using SELECT to get id, then get another column from another row - mysql

I have a table setup like this:
id (INT) | display_name (VARCHAR) | referral (INT)
referral is the id of the referral user. Is there a way that I can pull the referral ID, then use that to get the display_name of the referring member in one call? Or is it easier to do with two separate calls?

Essentially, all you'd need to do is JOIN your table on itself.
SELECT child.*, parent.display_name AS referral_name
FROM users AS child
JOIN users AS parent ON child.referral = parent.id
WHERE child.id = 2
This aliases the users table as child, then joins on the same table (aliased as parent).
+----+--------------+----------+---------------+
| id | display_name | referral | referral_name |
+----+--------------+----------+---------------+
| 2 | Jim | 1 | Tim |
+----+--------------+----------+---------------+
If the referral relationship may not exist, then you can use a LEFT JOIN instead:
SELECT child.*, parent.display_name AS referral_name
FROM users AS child
LEFT JOIN users AS parent ON child.referral = parent.id
+----+--------------+----------+---------------+
| id | display_name | referral | referral_name |
+----+--------------+----------+---------------+
| 1 | Tim | NULL | NULL |
| 2 | Sam | 1 | Tim |
| 3 | Kimberly | 1 | Tim |
+----+--------------+----------+---------------+

The following query will do what you want by joining the table with itself.
select t2.display_name from table_name t1 join table_name t2 on t1.referral=t2.id
where table_name is your table's name.

Related

MySQL - Selecting Duplicates across 3 columns and joining with another table to filter

I have a Purchases table, where I'm trying to select all rows where first name, surname and email are duplicates (for all 3).
Purchases table:
| purchase_id | product_id | user_id | firstname | surname | email |
| ------------- | -----------| ------------- | ----------- | --------- | ----------- |
| 1 | 1 | 777 | Sally | Smith | s#gmail.com |
| 2 | 2 | 777 | Sally | Smith | s#gmail.com |
| 3 | 3 | 777 | Sally | Smith | s#gmail.com |
| 4 | 1 | 888 | Bob | Smith | b#gmail.com |
Further to this, each product ID corresponds to a product type in a 'Products' table, and I'm trying to filter by 'lawnmower' purchases (so only product ID 1 & 2)
Products table:
| product_type | product_id |
| ------------- | -----------|
| lawnmower | 1 |
| lawnmower | 2 |
| leafblower | 3 |
I'm hoping to write a query that will return all purchases of the 'lawnmower' type where first name, last name, and email are duplicates (so would return the first two rows of the Purchases table).
This is where my query is at so far, however it's not returning accurate data (e.g. I know I have around 350 duplicates and it's returning 10,000 rows):
SELECT t. *
FROM database_name.purchases t
JOIN (
SELECT firstname, surname, email, count( * ) AS NumDuplicates
FROM database_name.purchases
GROUP BY firstname, surname, email
HAVING NumDuplicates >1
)tsum ON t.firstname = tsum.firstname
AND t.surname = tsum.surname
AND t.email = tsum.email
INNER JOIN database_name.products p2 ON t.product_id = p2.product_id
WHERE p2.product_type = 'lawnmower'
Just wanting to know what I need to tweak in my query syntax.
You know that you should be returning Sally Smith. Create a table from the results of your query above. Then Select * from that table where first_name=sally and surname=Smith. See if you can figure out where you are going wrong based on that. This will help you debug these type of issues yourself in the future.
Your inner SELECT does not filter on the product type. It gets all customers who have purchased any two items. Then you join it to purchases and therefore also get the purchases of customers who have bought any two items and, possibly only one, lawnmower. Add a filter on the product type in the subquery too:
SELECT t.*
FROM database_name.purchases t
INNER JOIN (SELECT purchases.userid
FROM database_name.purchases
INNER JOIN database_name.products
ON products.product_id = purchases.product_id
WHERE products.product_type = 'lawnmower'
GROUP BY userid
HAVING count(*) > 1) s
ON t.user_id = s.user_id
INNER JOIN database_name.products p
ON t.product_id = p.product_id
WHERE p.product_type = 'lawnmower';
Your schema also is problematic -- denormalised. firstname, surname and email depend on user_id (Note that I only grouped and joined using the user_id, that's enough,). So they shouldn't be in purchases, only user_id. product_type better by an ID referencing to some product type table too.

How to use JOIN instead of comma?

I have this query:
INSERT INTO Votes (id_post,id_user)
SELECT ?,?
FROM Posts p, Users u
WHERE p.id_user = :id_author
AND u.id = $_SESSION['id']
AND u.active = 1
limit 1;
Now I want to use JOIN instead of ,. But there isn't any common column between those two tables. So what should I write in ON clause?
What I'm trying to do:
I have three tables:
// Posts
+----+----------+---------------+-----------+
| id | title | content | id_author |
+----+----------+---------------+-----------+
| 1 | title1 | content1 | 1234 |
| 2 | title2 | content2 | 5678 |
+----+----------+---------------+-----------+
// ^ the id of post's author
// Users
+----+--------+--------+
| id | name | active |
+----+--------+--------+
| 1 | jack | 1 |
| 2 | peter | 0 |
| 3 | John | 1 |
+----+--------+--------+
// Votes
+----+---------+---------+
| id | id_post | id_user |
+----+---------+---------+
| 1 | 32 | 1234 |
| 2 | 634 | 5678 |
| 3 | 352 | 1234 |
+----+---------+---------+
// ^ the id of current user
Now I need to check two conditions before inserting a new vote into Votes table:
Is the id of author the same as what I pass as id_author? Posts.id_user = :id_author (I know I can do that by a FK, but I don't want)
The account of current user is active? Users.active = 1
Sum Up: I'm trying to don't let people be able to vote who are inactive (active = 0). For example if Stackoverflow bans you, then you cannot vote to posts anymore, because you (current user) are banned. So I'm pretty sure $_SESSION['id'] should be used in the query to determine current user.
I suggest using exists instead of join:
INSERT INTO Votes (id_post, id_user)
SELECT id_post, id_user FROM (SELECT ? id_post, ? id_user) a
WHERE EXISTS (
SELECT 1 FROM Users
WHERE id = ?
AND active = 1
) AND EXISTS (
SELECT 1 FROM posts
WHERE id_user = :id_author
)
You already have a join here! This is an implicit join.
INNER JOIN and , (comma) are semantically equivalent in the absence of
a join condition: both produce a Cartesian product between the
specified tables (that is, each and every row in the first table is
joined to each and every row in the second table).
So there isn't a need for you to 'introduce' a join here.

Getting "subquery returns more than one row" error whenever trying to join a column from another table as alias?

I am stuck with this problem for a whole 2 days. I have a users table and it contains:
+--------+--------+----------+--------------------------------------------+
| userId | name | username | profile_pic |
+--------+--------+----------+--------------------------------------------+
| 1 | john | john123 | http://localhost/profile_pic/user1pic.jpg |
| 2 | andrew | andrew | http://localhost/profile_pi/user2pic.jpg |
| 3 | doe | doe | http://localhost/profile_pic/user3pic.jpg |
+--------+--------+----------+--------------------------------------------+
I have another table called userpost which contains:
+--------+--------+-------------+----------------------------+
| postId | userId | postMessage | postImage |
+--------+--------+-------------+----------------------------+
| 1 | 1 | "Hey" | http://localhost/post1.jpg |
| 2 | 3 | "Add me" | http://localhost/post2.jpg |
| 3 | 2 | "boring" | http://localhost/post3.jpg |
+--------+--------+-------------+----------------------------+
userId is refrenced to users.userId. I am trying to join profile_pic to userpost but mysql is returning error. Here is what I am doing:
SELECT *, (SELECT profile_pic FROM users
INNER JOIN userpost on users.userId = userpost.userId) as profile_pic FROM userpost
But getting Subquery returns more than 1 row error
I know I am doing something stupid with the query. I just want something like this:
+--------+--------+-------------+----------------------------+--------------------------------------------+
| postId | userId | postMessage | postImage | profile_pic |
+--------+--------+-------------+----------------------------+--------------------------------------------+
| 1 | 1 | "Hey" | http://localhost/post1.jpg | http://localhost/profile_pic/user1pic.jpg |
| 2 | 3 | "Add me" | http://localhost/post2.jpg | http://localhost/profile_pic/user3pic.jpg |
| 3 | 2 | "boring" | http://localhost/post3.jpg | http://localhost/profile_pi/user2pic.jpg |
+--------+--------+-------------+----------------------------+--------------------------------------------+
I am having a meeting tomorrow to showcase my prototype app. Help will be appreciated.
You are using a sub query not a join. When using subquery in the select, you have to make sure it returns exacly one row like
SELECT COL1,COL2,(SELECT 1) from YourTable
Or by using a correlated query, which I assume was your purpose but is not required since its from the same table as you select, so just use a simple join:
SELECT s.*, t.profile_pic
FROM users t
INNER JOIN userpost s on t.userId = s.userId
Try this...
SELECT *,
(SELECT top 1 profile_pic FROM users a
where a.userId = b.userId order by b.postId desc) as profile_pic
FROM userpost b
But i am not sure, the above query returns the desired profile picture.
You are not using the sub-query correctly.you should make sure that sub-query will return only one row.
Below is the Query that you require.
Select up.postId,u.userId,up.postMessage,up.postImage,u.profile_pic from user u
inner join userpost up on u.userId=up.userId

MySQL showing duplicate rows

I have two tables, people and comment.
Table: people
+-------------------+----------------+-------------+------+
| id | cn | en | dob | role |
+-------------------+----------------+-------------+------+
| 1 | ChineseName | EnglishName | 1989-03-02 | 0 |
+-------------------+----------------+-------------+------+
| 2 | ChineseName2 | EnglishName2 | 1923-06-12 | 1 |
+-------------------+----------------+-------------+------+
Table: comment
+----+--------+----------+-------------------+---------------------+
| id | owner | owner_id | creator_person_id | comment |
+----+--------+----------+-------------------+---------------------+
| 1 | PERSON | 2 | 1 | Some comments here |
+----+--------+----------+-------------------+---------------------+
| 2 | TRANSAC| 1 | 1 | Comments |
+----+--------+----------+-------------------+---------------------+
| 3 | PERSON | 1 | 1 | Comments here |
+----+--------+----------+-------------------+---------------------+
When I execute the query:
SELECT comments.comment,
creator_person.id AS creator_id,
creator_person.cn AS creator_cn,
creator_person.en AS creator_en,
creator_person.dob AS creator_dob,
creator_person.role AS creator_role
FROM people, comments
JOIN people AS creator_person
ON comments.creator_person_id = creator_person.id AND comments.owner = 'PERSON' AND comments.owner_id = 1
ORDER BY people.id
I suppose it will return me only 1 row, however I'm getting a duplicate of that row:
+------------------+------------+-------------+-------------+-------------+--------------+
| comment | creator_id | creator_cn | creator_en | creator_dob | creator_role |
+------------------+------------+-------------+-------------+-------------+--------------+
| Comments here | 1 | ChineseName | EnglishName | 1989-03-02 | 0 |
+------------------+------------+-------------+-------------+-------------+--------------+
| Comments here | 1 | ChineseName | EnglishName | 1989-03-02 | 0 |
+------------------+------------+-------------+-------------+-------------+--------------+
you joining people table twice. So you can modify your query to become:
SELECT comments.comment,
creator_person.id AS creator_id,
creator_person.cn AS creator_cn,
creator_person.en AS creator_en,
creator_person.dob AS creator_dob,
creator_person.role AS creator_role
FROM comments
INNER JOIN people AS creator_person
ON comments.creator_person_id = creator_person.id
AND comments.owner = 'PERSON' AND comments.owner_id = 1
ORDER BY creator_person.id
also add distinct keyword if same problem occur like:
SELECT distinct comments.comment, ...
...
...
You should change:
FROM people, comments
to:
FROM comments
Since you are joining on the people table you do not need to include it in the FROM clause.
You will also need to update your order clause to reflect the alias name you gave the people table:
ORDER BY creator_person.id
You have an extra join in there (an implicit one in people,comments since you are after doing an inner join with people you should remove the first).
select comments.comment,
creator_person.id as creator_id,
creator_person.cn as creator_cn,
creator_person.en as creator_en,
creator_person.dob as creator_dob,
creator_person.role as creator_role
from comments
inner join people as creator_person
on comments.creator_person_id = creator_person.id
where comments.owner = 'PERSON' and comments.owner_id = 1
order by creator_person.id;
sqlfiddle demo
I also moved the other validations from the on clause to the where clause. In this case, you are doing a inner join, it wouldn't make a difference, but this is a bad habit and could make you do weird things when you use left/right joins in the future

What's the proper way to exclude this group from my MySQL Query?

Table 1: Users
| profile_id | name |
------------------------
| 1 | Joe |
| 2 | Jane |
| 3 | Jill |
| 4 | Jeffery |
Table 2: User and Role to Team Lookup
| team_id | profile_id | role |
---------------------------------
| 1 | 1 | coach |
| 1 | 2 | player |
| 2 | 4 | coach |
| 2 | 1 | player |
The scenario is that Jill is building a team, and the restriction is that you cannot be a player on more than one team. So I'm trying to build a query that pulls up those eligible to join Jill's team.
My first attempt was:
SELECT `users`.`profile_id`
FROM `users` LEFT JOIN `user_role_to_team_lookup` AS `utr` USING(`profile_id`)
WHERE `utr`.`role` != 'player' OR `utr`.`role` IS NULL
The problem is that because Joe is a coach, he matches the criteria~ even though he is also already a player.
What would be the proper way to exclude users that are already players from the result set?
I would write this without the subquery that most people use:
SELECT u.profile_id
FROM users AS u
LEFT OUTER JOIN user_role_to_team_lookup AS utr
ON u.profile_id = utr.profile_id AND utr.role = 'player'
WHERE utr.profile_id IS NULL
In other words, look for a user who is already a player. Those who aren't a player will match no rows in the outer join, and therefore any column of utr will be NULL.
But it's best to put the condition in the ON clause of the join.
SELECT u.profile_id
FROM users u
WHERE NOT EXISTS(SELECT 1
FROM user_role_to_team_lookup urtl
WHERE urtl.profile_id = u.profile_id
AND urtl.role = 'player')
You can probably do this:
SELECT profile_id FROM users
WHERE profile_id NOT IN (SELECT DISTINCT profile_id FROM utr WHERE role = 'player');
SELECT profile_id
FROM users
WHERE profile_id NOT IN (
SELECT profile_id
FROM user_role_to_team_lookup
WHERE role = 'player');