How to use JOIN instead of comma? - mysql

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.

Related

SQL. Count rows where 2 columns foreigns is same type

I have such a task, and I can't solve it.
I have these tables:
users:
id | type
transactions:
id | sender_id | receiver_id
How can I make a select that will result this for each transactions row:
id | sender_id | receiver_id | sender_type | receiver_type
Tried JOIN but no success, I get only sender_id, receiver_id, type.
How can I get type column two times, for each one?
ex:
users:
id | type
1 | 4
2 | 3
3 | 1
4 | 3
transactions:
id | sender_id | receiver_id
1 | 2 | 3
2 | 1 | 4
3 | 3 | 1
result:
id | sender_id | receiver_id | sender_type | receiver_type
1 | 2 | 3 | 3 | 1
2 | 1 | 4 | 4 | 3
3 | 3 | 1 | 1 | 4
The trick you need for this is to JOIN the users table twice to the transactions table.
You'll need aliases to do this. I've used the one-letter aliases r and s. This is a very common application of the JOIN operation. It works as if there were two copies of the table.
It helps to create aliases for the column names too; some SQL clients work strangely when you feed them result sets with duplicate column names.
SELECT t.id, t.sender_id, t.receiver_id,
s.type sender_type,
r.type receiver_type
FROM transactions t
JOIN users s ON t.sender_id = s.id
JOIN users r ON t.receiver_id = r.id
ORDER BY t.id
You need to join to the users table explicitly for each type (sender and receiver), so for example:
SELECT
tr.id,
tr.sender_id,
tr.receiver_id,
us_sn.type,
us_rx.type
FROM
transactions tr
JOIN
users us_rx ON tr.receiver_id = us_rx.id
JOIN
users us_sn ON tr.sender_id = us_sn.id

Querying MySQL tables for a item a user hasnt 'scored' yet

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

Select a MYSQL result where tables one and two match and two and three do not match

I have a two tables with users in an old format and a new format. I want to match the users with the old format to a separate table, then exclude all users who also show up in the new user format table. My data is like this:
Table newUsers:
+----+-------+-------+----------+
| id | oldid | first | last |
+----+-------+-------+----------+
| 1 | 10 | John | Kennedy |
| 2 | 66 | Mitch | Kupchak |
+----+-------+-------+----------+
Table posts:
+----+---------+
| id | user_id |
+----+---------+
| 1 | 10 |
| 1 | 66 |
| 1 | 88 |
| 2 | 88 |
| 2 | 28 |
| 3 | 10 |
+----+---------+
Table oldUsers:
+----+----------+-------+----------+
| id | username | first | last |
+----+----------+-------+----------+
| 10 | A | John | Kennedy |
| 66 | B | Mitch | Kupchak |
| 88 | C | Dale | Earnhardt|
+----+----------+-------+----------+
Result wantend:
+----+----------+-------+----------+
| id | username | first | last |
+----+----------+-------+----------+
| 88 | C | Dale | Earnhardt|
+----+----------+-------+----------+
I want to select my result by specifying: posts.id = 1 and posts.user_id = oldUsers.id and newUsers.oldid != oldUsers.id so that I only receive oldUser.id equaling 88 because he wasn't in the newUsers list.
I have tried all kinds of JOINS and SUBQUERIES. I keep getting all of the results and not the results minus corresponding entries in the newUsers table.
select * from oldusers where id in
select * from
(select id from oldusers where id in
select distinct userid from posts where id=1)
where id not in (select oldid from newusers);
Here is a way to do it
select
o.* from oldUsers o
left join newUsers n on o.id = n.oldid
left join posts p on n.oldid = p.user_id or o.id = p.user_id
where n.id is null and p.id= 1;
For better performance add the following indexes
alter table newUsers add index oldid_idx(oldid);
alter table posts add index user_post_idx (id,user_id);
I ended up finding my answer on my own and then came here to find others tried. Abhik's code did work, but was too inefficient to use. I ended up playing with my own code and IS NULL until I found something that was much more efficient.
select o.* from posts p, oldUsers o
LEFT JOIN newUsers n ON o.id = n.oldid
WHERE p.user_id = o.id AND p.id = 1 AND n.id IS NULL
Executes in .0044 seconds. Something I can use on a production site.
With indexes added from previous answer it now executes in .001x seconds so definately going with my own code.

Match data against one column in mysql

Here is the sqlFiddle
I want to filter the users who have selected entities ,So if I want to filter user with entity say entity having ids "1" and "3" I hope to get the users which have both of these entities.
No of entities selected can vary in number .
Query I am using is
SELECT user_id from user_entities where entity_id IN(1,3)
but for obvious reason it is returing me result as
+----+-----------+---------+--------+
| ID | ENTITY_ID | USER_ID | STATUS |
+----+-----------+---------+--------+
| 1 | 1 | 3 | 1 |
| 2 | 3 | 3 | 1 |
| 7 | 1 | 2 | 1 |
| 29 | 3 | 1 | 1 |
+----+-----------+---------+--------+
So I will apply distinct to it it will give me user id with ids 1,2,3 but I only want user 3 as this is the only user having both entities .
What can be modified to get the exact results
You could join the table to itself specifying both IDs as part of the join condition:
SELECT e1.user_id
FROM user_entities e1
INNER JOIN user_entities e2
ON e1.user_id = e2.user_id AND
e1.entity_id = 1 AND
e2.entity_id = 3;

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');