I'm working on a project for my University with Rails 3/PostgreSQL, where we have Users, Activities and Venues. An user has many activities, and a venue has many activities. An activity belongs to an user and to a venue and has therefore an user_id and a venue_id.
What I need is a SQL query (or even a method from Rails itself?) to find mutual venues between several users. For example, I have 5 users that have visited different venues. And only 2 venues got visited by the 5 users. So I want to retrieve the 2 venues.
I've started by retrieving all activities from the 5 users:
SELECT a.user_id as user, a.venue_id as venue
FROM activities AS a
WHERE a.user_id=116 OR a.user_id=227 OR a.user_id=229 OR a.user_id=613 OR a.user_id=879
But now I need a way to find out the mutual venues.
Any idea?
thx,
tux
I'm not entirely familiar with sql syntax for postgresql, but try this:
select venue_id, COUNT(distinct user_id) from activities
Where user_id in (116,227,229,613,879)
group by venue_id
having COUNT(distinct user_id) = 5
EDIT:
You will need to change the '5' to however many users you care about (how many you are looking for).
I tested this on a table structure like so:
user_id venue_id id
----------- ----------- -----------
1 1 1
2 6 2
3 3 3
4 4 4
5 5 5
1 2 6
2 2 7
3 2 8
4 2 9
5 2 10
The output was:
venue_id
----------- -----------
2 5
You would have to come up with some parameters for your search. For example, 5 user may have 2 Venues in common, but not 3.
If you want to see what Venues these five users have in common, you can start by doing this:
SELECT a.venue_id, count(1) as NoOfUsers
FROM activities AS a
WHERE a.user_id=116 OR a.user_id=227 OR a.user_id=229 OR a.user_id=613 OR a.user_id=879
group by a.venue_id
That would bring you, for those users, how many users have that venue. So you have degrees of "Venue sharing".
But if you want to see ONLY the venues who were visited by the five users, you'd add a line in the end:
SELECT a.venue_id, count(1) as NoOfUsers
FROM activities AS a
WHERE a.user_id=116 OR a.user_id=227 OR a.user_id=229 OR a.user_id=613 OR a.user_id=879
group by a.venue_id
having count(1) = 5 --the number of users in the query
You should also consider changing your WHERE statement from
WHERE a.user_id=116 OR a.user_id=227 OR a.user_id=229 OR a.user_id=613 OR a.user_id=879
to
WHERE a.user_id in (116, 227, 229, 613, 879)
in sql it would be something like:
Select distinct v.venue_id
from v.venues
join activities a on a.venue_id = v.venue_id
Join users u on u.user_id = a.user_id
Where user_id in (116,227,229,613,879)
You need to join up your tables so to get all the venues that have had activities that have had users. When you are just learning it is sometimes simpler to visualize it if you use subqueries. At leasts thats what I found for me.
Related
Suppose that I have 3 tables,
student(user_id, occupation)
thumbsup(user_id, post_id)
post(post_id, user_id)
I need to compute the number of good posts that one student has.
The conditions are like this
a post can receive multiple thumbs up
but once it receives at least one thumbup it is a goodpost
goodposts are how many posts that one user posted that received thumbsup
For example, if user_id 1 posts 3 posts with post_id, 3,4,5, 6 and post_id 3 receives 2 thumbs up, and post_id 4 receives 5 thumbs up and post_id 5 receives 10 thumbs up, 6 receives 0 thumbs up, that means user_id 1 has 3 good posts because 6 in this case has no thumbs up
I can sccuessfully project this by doing this. But there is one additional requirement:
if a student does not have any good posts, their number of good posts should be zero.
This is my current solution that projects student if they have good posts
(SELECT temp2.user_id, S.occupation, goodpost
FROM
(SELECT P.user_id, COUNT (P.user_id) as goodpost
FROM
(SELECT T.post_id, Count(*) AS likes
FROM swoosh.thumbsup T
group by T.post_id) As TEMP, swoosh.post P
WHERE P.post_id = temp.post_id
GROUP BY P.user_id) temp2, swoosh.Student S
WHERE temp2.user_id = S.user_id)
But I need to project those user_id who dont receive thumbs up too in this table. What is the approach
this is my first post so bear with me.
I've been working on a problem with a large data set for about a week now and I am banging my head against the wall. Essentially, I have a database containing records of each time a user accesses a service; each record has a unique ID associated with the user (user_id), an assigned country tag which may differ between accesses (demo_tag) which is a best-guess about a users' geolocation, and a bunch of other information I'm not currently worried about.
What I want to accomplish is to determine which country a user most likely resides in, based on the number of times they've accessed the service with a certain assigned country. In the event of a tie, I want to retrieve BOTH regions (say, a user has logged in equal numbers of times from both France and Belgium, I want to associate the user with both countries). Basically for each user, I want to know the maximum number of times they've logged in from one specific location, and which location(s) it is/they are.
e.g. If I had:
user_id region
1 USA
1 CAN
1 CAN
2 MEX
2 MEX
2 USA
2 USA
I'd expect to get back:
user_id region count
1 CAN 2
2 MEX 2
2 USA 2
Right now I have a very ugly, multi-nested query and I feel there must be a better way to do this. Any advice?
Use group by and rank():
select ur.*
from (select user_id, region, count(*),
rank() over (partition by user_id order by count(*) desc) as seqnum
from t
group by user_id, region
) ur
where seqnum = 1;
We have a table in Redshift:
people
people_id people_tele people_email role
1 8989898332 john#gmail.com manager
2 8989898333 steve#gmail.com manager
3 8989898334 andrew#gmail.com manager
4 8989898335 george#gmail.com manager
I have a few users who would query the table like:
select * from people where role = 'manager' limit 1;
The system users are basically phone calling these people for up-selling products. So, when the query return results, it should not return same people ever.
For ex.
If User A executes the query - select * from people where role = 'manager' limit 1;, then he should get the result:
people_id people_tele people_email role
1 8989898332 john#gmail.com manager
If User B executes the query - select * from people where role = 'manager' limit 1;, then he should get the result:
people_id people_tele people_email role
2 8989898333 steve#gmail.com manager
APPROACH 1
So, I thought of adding a is_processed column to not return the same results. So, after User A executes the query, the table would look something like:
people_id people_tele people_email role is_processed
1 8989898332 john#gmail.com manager 1
2 8989898333 steve#gmail.com manager 0
3 8989898334 andrew#gmail.com manager 0
4 8989898335 george#gmail.com manager 0
APPROACH 2
Another thought was to create another table called - query_history where I have:
query_id people_id processed_time
1 1 22 Jan 2020, 4pm
2 2 22 Jan 2020, 5pm
QUESTION
My question is what happens when User A and User B queries at the EXACT same time? The system would return the same people_id at that moment and 2 phone calls would be made to the same person.
How can I solve the concurrency problem?
You can solve it with your Approach 1 only with adding Randomisers in it
SELECT * FROM people
WHERE role = 'manager'
AND is_processed = 0
order by random()
limit 1;
Refer: https://docs.aws.amazon.com/redshift/latest/dg/r_RANDOM.html
Maybe you can solve with transactions ? try some try/catch maneuver.
Transaction MySQL
edit: Sorry, for some reason i thought you are working with MySQL. https://docs.aws.amazon.com/redshift/latest/dg/stored-procedure-transaction-management.html
profile
---
id name
1 John
2 Jane
3 Jill
...
swipe
---
id profile_1_id profile_2_id liked
1 2 1 true
2 3 1 false
...
If you've used Tinder before, you might recognize that it seems to fetch an initial card deck that consists of:
users who already like you that you can instantly match with, pushed to the top
other users
(out of scope for this question but it also sprinkles in some more attractive users)
If we extend the example to 100+ users, id=1 John was looking at the app, and we fetched with a limit of 20, it would guarantee Jane comes back (since Jane already likes John and John could match right away) + 19 others to fill the rest of John's deck to keep John swiping for more.
What is the SQL for "get people who like John first then fill the rest with random users"? Would this be a WHERE(case if else) or some other statement?
Here is a query that should meet your need.
It works by using a conditional sorting with CASE. Users that liked John will are given higher priority, and will appear sorted by id. Other users are given a lower, random, priority ; this also means, for a given user, this part of list will not always be the same (which, I believe, fits your purpose). The number of output records is then controlled by a LIMIT clause.
I tested the query in this db fiddle. You need to replace the question mark (?) in the CASE clause with the id of user for which you are generating a card (1 for John in your sample data).
SELECT
p.id,
p.name
FROM
profile p
LEFT JOIN swipe s on s.profile_1_id = p.id
ORDER BY
CASE s.profile_2_id
WHEN ? THEN 0
ELSE FLOOR(random() * 10) + 1
END,
p.id
LIMIT 20
You could try something like this but I think you're oversimplifying. Do you want to exclude not liked people from the others?
select * from profile p
left outer join swipe s on (p.id=profile_1_id and s.profile_2_id = 1 and liked = true)
where
p.id<>1
order by coalesce(profile_2_id , random()*-1000000) desc
limit 20
I have two classes Apartment and AdditionalSpace representing tables as below.
Apartment table
ID AREA SOLD
---- ------ ----
1 100 1
2 200 0
AdditionalSpace table
ID AREA APARTMENTID
---- ------ -----------
10 10 1
11 10 1
12 10 1
20 20 2
21 20 2
As you can see Apartment's table has a one-to-many relation with AdditionalSpace table, i.e. Apartment.ID=AdditionalSpace.APARTMENTID.
Question:- How to retrieve total area of a sold apartment including its additional space area.
The SQL which I have used so far to retrieve similar result is :-
select sum(apt.area + ads.adsarea) from apartment apt left outer join (select sum(area) as adsarea, apartmentid from additionalspace group by apartmentid) ads on ads.apartmentid=apt.id where apt.sold=1
I am struggling to find a way in order to implement the above scenario via criteria instead of SQL/HQL. Please suggest. Thanks.
I don't think this is possible in criteria. The closest I can see is to simply get the size of the apartment and the sum of the additional areas as two columns in your result, like this:
Criteria criteria = session.createCriteria(Apartment.class,"a");
criteria.createAlias("additionalSpaces", "ads");
criteria.setProjection(Projections.projectionList()
.add(Projections.property("area"))
.add(Projections.groupProperty("a.id"))
.add(Projections.sum("ads.area")));
Alternatively, if you still want to use Hibernate but are happy to write it in HQL, you can do the following:
select ads.apartment.id,max(a.area)+sum(ads.area)
from Apartment a
join a.additionalSpaces ads
group by ads.apartment.id
This works because HQL allows you to write the + to add together the two projections, but I don't know that an analogous method exists on the projections api.