mysql with subquery on same table - mysql

i have a single mysql table with at minimum userId, wager.
Data can be
userId = 1 wager = 10.00, userId = 2 wager = 5.00, userId = 3 wager = 1.00.
I want to run a select with a userId param that returns all userId's that are <= the wager for that user
So if userId = 2 I get back all other users with a wager of 5.00 or less.
Query does a lot more than this with one join but just need some help or best approach with the above

You can do it with a self-join:
SELECT u1.*
FROM user AS u1
INNER JOIN user AS u2 ON u1.wager <= u2.wager
WHERE u2.userId = ?

SELECT userA.*
FROM user AS userA,user AS userB
WHERE userA.wager <= userB.wager
AND userA.userId=userB.userId
AND userB.userId = 2
It's a good question and hope it helps you. I think most people forgot to link the userId together and it may need to link the userId together.

Related

Optimisation of subqueries

I have a relation between users and groups. Users can be in a group or not.
EDIT : Added some stuff to the model to make it more convenient.
Let's say I have a rule to add users in a group considering it has a specific town, and a custom metadata like age 18).
Curently, I do that to know which users I have to add in the group of the people living in Paris who are 18:
SELECT user.id AS 'id'
FROM user
LEFT JOIN
(
SELECT user_id
FROM user_has_role_group
WHERE role_group_id = 1 -- Group for Paris
)
AS T1
ON user.id = T1.user_id
WHERE
(
user.town = 'Paris' AND JSON_EXTRACT('custom_metadata', '$.age') = 18
)
AND T1.user_id IS NULL
It works & gives me the IDs of the users to insert in group.
But when I have 50 groups to proceed, like for 50 town or various ages, it forces me to do 50 requests, it's very slow and not efficient for my Database.
How could I generate a result for each group ?
Something like :
role_group_id user_to_add
1 1
1 2
2 1
2 3
The only way I know to do that for now is to do an UNION on several sub queries like the one above, but of course it's very slow.
Note that the custom_metadata field is a user defined field. I can't create specific columns or tables.
Thanks a lot for your help.
if I good understood you:
select user.id, grp.id
from user, role_group grp
where (user.id, grp.id) not in (select user_id, role_group_id from user_has_role_group) and user.town in ('Paris', 'Warsav')
that code give list of users and group which they not belong from one of towns..
To add the missing entries to user_has_role_group, you might want to have some mapping between those town names and their group_id's.
The example below is just using a subquery with unions for that.
But you could replace that with a select from a table.
Maybe even from role_group, if those names correlate with the user town names.
insert into user_has_role_group (user_id, group_id)
select u.user_id, g.group_id
from user u
join (
select 'Paris' as name, 1 as group_id union all
select 'Rome', 2
-- add more towns here
) g on (u.town = g.name)
left join user_has_role_group ug
on (ug.user_id = u.user_id and ug.role_group_id = g.group_id)
where u.town in ('Paris','Rome') -- add more towns here
and json_extract(u.custom_metadata, '$.age') = 18
and ug.id is null;

Suggest users based on common interest - MySql query

Basically what i am trying to do is to suggest people based on common interests.
I have a table of Users(id, username, firstname, lastname, etc)
I have a table of Interested_People where UserID + Interested_in is stored.
I have a table of Contactlist where people who are added with each other is stored.(user1, user2, accepted [1,0])
What I want is to select * users table who are not my friend and they have same interest with me as well.
I searched a lot in internet but couldn't find something like so.
Here i do have created a query and it does exactly what I want. But it is very slow. Even it takes 16 to 20 second to output in PHPMyAdmin in my local machine. Now I Kindly request you guys if you can edit my query a bit and make it bandwidth & time efficient.
SELECT *
FROM users
WHERE id IN(SELECT userid
FROM interested_people
WHERE interested_in IN(SELECT interested_in
FROM interested_people
WHERE userid = [userid])
AND id NOT IN(SELECT user1 AS my_friends_userid
FROM contactlist f
WHERE f.user2 = [userid]
AND accepted = 1
UNION
SELECT user2 AS my_friends_userid
FROM contactlist f
WHERE f.user1 = [userid]
AND accepted = 1))
AND id != [userid]
ORDER BY Rand ()
LIMIT 0, 10;
[Userid] in this query is the ID of the user who is online. Like if i m online my ID will be 1.
This query suggest 10 random users who are not my friends and have same interests as me. But very slooow.
Thanks in advance!
Your problem suggests a self-join to get the users with common interests. Then, not exists to avoid the contact list. The following gets the list of users with common interests, ordered by the number of common interests:
select ip2.userid, count(*) as numInCommon
from interested_People ipme join
interested_People ip2
on ipme.interested_in = ip2.interested_in and
ipme.userid = $UserId and -- Your user id goes here
ip2.userid <> ipme.userid
where not exists (select 1
from contactlist cl
where cl.user1 = ipme.userid and cl.user2 = ip2.userid and
cl.accepted = 1
) and
not exists (select 1
from contactlist cl
where cl.user1 = ip2.userid and cl.user2 = ipme.userid and
cl.accepted = 1
)
group by ip2.userid;

Using select statement with two tables

I have two tables. One contains User and company relationship a show below
User_company
UserId CompanyId
1 2
2 1
3 1
4 2
Another table holds user information
User
Id Name City
1 Peter LA
2 Harry SF
3 John NY
4 Joe CI
How do I make a statement which will give me All the users which are in company 1? Will something like
Select * from User where Id in (Select UserId from User_company where CompanyId = 1)
work?
SELECT * from User
left join User_company on User_company.UserId=User.Id
This would work...
SELECT * works but can be sluggish over time as it may not scale well with more data.
FROM User
WHERE Id in (Select UserId from User_company where CompanyId = 1)
So would this.. - best if you need data from both tables.
SELECT *
FROM User U
INNER JOIN User_Company UC
ON U.ID = UC.UserID
WHERE UC.CompanyID = 1
As would this - Probably the fastest if you just need data from user table.
Select * from User U
where exists (Select * from User_Company UC where U.ID = UC.UserID and CompanyID = 1)
OUTER joins are only needed if you need all records from one table and only those that match in another.
As to which is the best above: it depends on existing indexes and other requirements. Any of the above will return what's been asked for.
Try this
Select u.*
from User u
inner join User_company uc
on u.Id = uc.UserId
and uc.CompanyId = 1
BTW, what's wrong with the query you have posted? It will work as well fine. Just that it's a subquery and you better replace it with Join for performance.
Select * from User where Id in
(Select UserId from User_company where CompanyId = 1)
SELECT U.* FROM User AS U LEFT JOIN
User_company AS UC ON U.Id = UC.UserId WHERE UC.CompanyId = 1

SQL query to find number of users who are in two specific groups

I have table in MySQL database with two integer columns for example:
userid | groupid
10 | 300
11 | 300
11 | 301
12 | 302
Given two groupids, I am looking for the best and quickest way to find userids which are in both groups. My table contains 23M rows and I need to that for each distinct pair of groupids. Currently both columns are indexed however it takes so long to get the result even for a single pair of groups and I have 1000 distinct groupids. The query I am running now is:
select count(t2.userid)
from usergroup t1, usergroup t2
where t1.groupid = 27 and t2.groupid = 714 and t1.userid = t2.userid
Is there a way to do it fast?
Why the join?
select
u.userid
from
usergroup u
where
u.groupid in (27, 714)
group by
u.userid
having
count(u.userid) > 1
Assuming a combination of userid and groupid is unique, which I figure it should be in a table like this.
It looks correct method for me, but it could be faster by creating prepared statements.
See the below post for example.
How can I prevent SQL injection in PHP?
I think this might be what you are looking for...
select
u1.userID
from
usergroup u1
join usergroup u2
on u2.groupid = 714
AND u1.userid = u2.userid
where
u1.groupid = 27
So, the primary WHERE clause is just give me a list of users within group ID = 27... so this will be optimized exclusively on the GROUP ID. THEN, by doing a self-join to the user groups table matched by the same user ID AND group ID = the 714, it will only return the record if such IS found. I could have a compound index on BOTH Group AND User such as
KEY GroupUser (groupid, userid)
so this way the index will be optimized for BOTH query components...
At MOST, it will go through the first u1 instance one time for everyone in the group 27... No counts or having involved...

Mysql join query

I have table users and another table premium_users in which I hold the userid and the date when he bought premium membership.
How can I use mysql join , so that in a single query I can select all the columns from the table users and also know for each premium user the date he joined on.
USERS:
ID USERNAME
1 JOHN
2 BILL
3 JOE
4 KENNY
PREMIUM USERS:
ID USERID DATE
1 2 20/05/2010
2 4 21/06/2011
And the final table (the one that will be returned my the query) should look like this:
ID USERNAME DATE
1 JOHN
2 BILL 20/05/2010
3 JOE
4 KENNY 21/06/2011
Is it ok for some rows to have the DATE value empty?
How can I check if that value is empty? $row['date']=='' ?
EDIT:
This was only an example, but the users table has much more columns, how can I select all from users and only date from premium_users without writing all the columns?
select u.*, pu.DATE
from USERS u LEFT OUTER JOIN PREMIUM_USERS pu on
u.ID = pu.USERID
You can check if a row is empty with:
if (!$row['DATE'])
{
...
}
select USERS.ID, USERS.USERNAME, PREMIUM_USERS.DATE
from USERS
join PREMIUM_USERS on USERS.ID = PREMIUM_USERS.ID
order by USERS.ID
This is mssql syntax, but it should be pretty similar...
select *
from users u
left join premiumUsers p
on u.id = p.id
order by u.id asc
SELECT A.*, B.DATE
FROM USERS A
LEFT JOIN PREMIUIM_USERS B on A.ID=B.USERID
EDITED
It might be easier to have it all in one table. You can have nullable fields for isPremium(t/f) and premiumDate. you actually dont even need the isPremium field. just premiumDate if its null they are not premium and if it has value they are premium user and you have the date they joined.