postgresql: joining 3 tables - postgresql

*First post: apologies in advance if I left anything out. Please let me know and I'll update!
I’m looking for:
users who have not logged in over 30 days
where the user’s role does not equal ('owner', 'renter', 'manager')
and where the source does not equal 'internal'
I have 3 tables
last_login - has user login info (load_date, current_date, last_login)
user - has info about user being external vs internal (source)
role - which role user has (role_name)
I have to join 3 tables for my WHERE conditions. When I join the 3rd table , my results drop from 300 results to just 10 or so. I've checked the tables and I should get at least 200+ results.
Can anyone tell me what's wrong with my joins?
select u.id, u.username
from user as u
join last_login as ll
on u.username = ll.username
join role as r
on ll.username = r.username
where ll.loaddate = ll.current_date - 1
and ll.lastlogin < ll.current_date - 30
and u.source <> 'INTERNAL'
and r.role_name <> ('Owner', 'Renter', 'Manager')

Looking your query i suppose that you should use NOT IN instead of <>. Also try to use INNER or LEFT JOIN. Something like this for example:
select u.id, u.username
from user as u
left join last_login as ll
on u.username = ll.username
left join role as r
on ll.username = r.username
where ll.loaddate = ll.current_date - 1
and ll.lastlogin < ll.current_date - 30
and u.source NOT LIKE '%INTERNAL%'
and r.role_name NOT IN ('Owner', 'Renter', 'Manager')

Related

Postgresql, inner join or subquery or view?

I have the following tables:
user
car
dealer
user_metrics
- user_id (FK) (required)
- dealer_id (FK) (can be null)
- car_id (FK) (can be null)
- saved
- .... other columns
A user can save a car or a dealership, when that happens the user_metrics.saved is set to true and the related car_id or dealership_id is set (car_id and dealership_id are exclusive, only one is set for a row).
I want user A to be able to see all users that have saved the same cars / dealerships.
So, if user A has saved car 1, 2,3 and dealership 5,7, I want to get all users that have saved any of those cars / dealerships.
I thought about inner join on user_metrics, but, I am not sure how to write the entire query that would deliver on this.
What query would allow me to get all users that have saved any of the cars/dealerships a certain user has saved?
If I understand as well maybe the below query solve your problem.
First should find a list of user A has been reserved after that should search which of car or dealer used by another user
with user_saved_data as (
select um.*,
u.name,
...
from user_metrics um
inner join user u
on um.user_id = u.id
where um.saved = true
and u.id = $1 -- User id of user 'A' or any username (Or use other column for create custom condition)
)
select usd.name as current_reserved_user,
u.name as reserved_by_user,
d.*,
c.*
from user_metrics um
inner join user u on um.user_id = u.id
left join user_saved_data usd on usd.dealer_id notnull and usd.dealer_id = um.dealer_id
left join user_saved_data usd on usd.car_id notnull and usd.car_id = um.car_id
left join dealer d on um.dealer_id = d.id
left join car c on um.car_id = c.id

Join two postgresql queries

I have the following query
SELECT role_uuid FROM users WHERE email = 'email#domain.com'
I also have a roles table the following fields:
uuid
name
created_at
I'm hoping to have 1 query that gives lets me select the role by email and get the name and created_at field from the roles table.
I've tried things like this but I can't quite figure it out.
SELECT *
FROM ( SELECT * FROM users WHERE email = 'email#domain.com') AS A
JOIN ( SELECT * FROM roles WHERE uuid = A.role_uuid) AS B
WHERE A.role_uuid = B.uuid
You JOIN the two tables which gives you a table with all the fields from both source tables. Then you use WHERE to filter and SELECT to specify the fields that you want to be returned.
SELECT r.name, r.created_at
FROM users u JOIN roles r ON (u.role_uuid = r.uuid)
WHERE u.email = 'email#domain.com'
If you run into naming conflicts because of fields from both tables sharing the same name you can use AS to define fieldnames for the output columns:
SELECT r.name AS rolename, u.name AS username, r.created_at
FROM users u JOIN roles r ON (u.role_uuid = r.uuid)
WHERE u.email = 'email#domain.com'

Postgresql WHERE clause using conditional sub-queries

I have a situation where each of the clients has users and each user can access to information about one or more branches.
We also have sys admins who can see everything and in database don't have any sites assigned to them. It just says the user is sys admin, so our system does not restrict the access.
I need to make a database query where I extract the list of branches the user has access to, but if the user is sys admin, I want to extract the list of all branches in the system.
I was trying something like this, but it does not work:
Select sites.name, sites.id
FROM sites
WHERE
sites.id IN (
CASE
WHEN (select u.level FROM users "u" WHERE u.username = 'JohnBrown') ='ROLE_SYSTEM_ADMIN'
THEN
(select id FROM sites)
ELSE
(select s2.id FROM users_have_sites uhs2
left join users u2 ON u2.id = uhs2.user_id
left join sites s2 ON s2.id = uhs2.site_id
where u2.username = 'JonhBrown')
END
)
I am getting this error:
ERROR: more than one row returned by a subquery used as an expression
I think something like this would work for you:
SELECT s.name, s.id
FROM sites s
LEFT JOIN users_have_sites uhs ON uhs.site_id = s.id
LEFT JOIN users u ON u.id = uhs.user_id AND u.username = 'JohnBrown'
WHERE (CASE WHEN (SELECT u.level FROM users WHERE u.username = 'JohnBrown') = 'ROLE_SYSTEM_ADMIN'
THEN TRUE ELSE FALSE END
OR u.id IS NOT NULL);
The LEFT JOINs do not filter out records from the sites table like an INNER JOIN would, so any site that meets either of the conditions in the WHERE clause will be in the result. This means that if your subquery shows that the user is a sys admind or if there is a record for that user and site is found in the users_have_sites table, those sites will be in the result set.
EDIT: Another fairly easy to read solution would be something like this:
SELECT s.name, s.id
FROM sites s,
users_have_sites uhs,
users u
WHERE u.username = 'JohnBrown'
AND (u.level = 'ROLE_SYSTEM_ADMIN'
OR (s.id = uhs.site_id AND u.id = uhs.user_id))
GROUP BY s.name, s.id;
The downside of this query is that it uses implicit joins which are not used very much any more. They are generally seen as an older way of doing things and can be less efficient. This will join all rows of on table to all rows of another table and then all of your filtering (and what you would generally think of as join conditions) are all in the WHERE clause. These typed of joins can be less efficient but this one should not be as the WHERE clause makes sure that only 1 result per site.
I think that this does what you want:
select s.name, s.id
from sites s
inner join users u on u.username = 'JohnBrown'
where
u.level = 'ROLE_SYSTEM_ADMIN'
or exists (
select 1
from users_have_sites uhs
where uhs.site_id = s.id and uhs.user_id = u.id
)
Here is another version of the query that you may find easier to follow (I do):
select s.name, s.id
from users u
inner join sites s
on u.level = 'ROLE_SYSTEM_ADMIN'
or exists (
select 1
from users_have_sites uhs
where uhs.site_id = s.id and uhs.user_id = u.id
)
where u.username = 'JohnBrown'

TSQL - Select every row from Table A - include field if there is related row in Table B

I have 3 tables
Tags
TagID
TagName
Users
UserID
Username
UsersFollowingTags
UFTID
UserID
TagID
Many users can follow many tags.
I need a single SQL statement producing a result set that includes all tagID's & tagNames - and a field that says whether a UserID is following that tag.
I have tried left join - but if I include a WHERE userID = #userID - it doesn't output tags that the user doesn't follow.
The field indicating a related row could just be a bit - or the userID - flexible there.
thx
instead of putting the userid=#userId in a where clause, add this in the left join clause, and you will be fine.
If you put a where clause on a left joined table, this will do the same as an inner join.
so something like this if you want the maybe following username
select t.TagName, u.UserName
from Tags t
left join UsersFollowingTags uft on uft.TagID = t.TagID
left join Users u on u.UserId = uft.UserId and uft.userID = #userId
or if a UserID is enough
select t.TagName, uft.UserID
from Tags t
left join UsersFollowingTags uft on uft.TagID = t.TagID and uft.userID = #userId
or with a "bit"
select t.TagName, case when uft.UserID is null then 0 else 1 end as isfollowing
from Tags t
left join UsersFollowingTags uft on uft.TagID = t.TagID and uft.userID = #userId
You can use CASE WHEN EXISTS:
SELECT t.TagID,
t.TagName,
UserFollowing = CASE WHEN EXISTS(SELECT 1 FROM UsersFollowingTags uft
WHERE t.TagID = uft.TagID
AND utf.UserID = #userID)
THEN 'Yes, user follows this tag'
ELSE 'No, user does not follow this tag' END
FROM dbo.Tags t

user information and the course name with time spend in a course

Can anybody help me out with the code to get the user's firstname, lastname, the course name and the time spend by the user on that course in moodle 2.6? Using configurable reports doesnot give me the exact solution.
You can use the following sql to find the course participants.
SELECT u.id, u.username, u.firstname, u.lastname FROM mdl_user u JOIN (SELECT DISTINCT eu1_u.id FROM mdl_user eu1_u JOIN mdl_user_enrolments eu1_ue ON eu1_ue.userid = eu1_u.id JOIN mdl_enrol eu1_e ON (eu1_e.id = eu1_ue.enrolid AND eu1_e.courseid = 4) WHERE eu1_u.deleted = 0 AND eu1_u.id <> 1 ) e ON e.id = u.id LEFT JOIN mdl_user_lastaccess ul ON (ul.userid = u.id AND ul.courseid = 4) LEFT JOIN mdl_context ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = 30) ORDER BY u.lastaccess DESC;
There are two ways to find the time spend in a course:
If course completion is enabled and the user has completed the course, then the time spend in the course will be the difference between the timestarted and timecompleted fields in mdl_course_completions table.
You can also calculate the time spent in a course from moodle logs.
hope this helps.