I am trying to produce a list of user that have access to particular locations. The table in my database that records locations users have access to is backwards in my opinion. It list the location the users can not access. I have tried not in several different ways without any luck. The query just returns a blank results set. I've tried find something on here to help me and googled it but this has not helped...I always get blank results screen(nothing returned). I think the issue lies with the USER_LOCATION_EXCLUSION having several entries for each USER_ID for each corresponding location. I did not create this table structure but inherited it.
Scenario
Tables Columns
====================================================
APP_USER USER_ID, USER_NAME
LOCATION LOCATION_ID, LOCATION_NAME
USER_LOCATION_EXCLUSION USER_ID, LOCATION_ID
APP_USER and LOCATION tables both have IDs that are unique. These IDs are both used in USER_LOCATION_EXCLUSION (list locations users can not access) and can be used many times in this table according to the users access. I would like to produce report that has the USER_NAME and LOCATION_NAME they have access to.
What it sounds like you want is a Cartesian result LESS the exclusions... ie: for each user, assume giving them EVERY location access, then find out what they are excluded from...
select
PreQuery.User_ID,
PreQuery.User_Name,
PreQuery.Location_Name
from
( select
AU.User_ID,
AU.User_Name,
L.Location_ID,
L.Location_Name
from
App_User AU,
Location L ) PreQuery
LEFT JOIN USER_LOCATION_EXCLUSION ULE
on PreQuery.User_ID = ULE.User_ID
AND PreQuery.LOCATION_ID = ULE.LOCATION_ID
where
ULE.Location_ID = NULL
By doing a LEFT JOIN to the exclusion table, every record will ATTEMPT to be joined to the exclusion list... So, when it DOES find a match, that location ID will exist... when it does NOT exist, it will be null (satisfying your NOT Excluded from)
This also eliminates sub-select WHERE clause tested for every user / location
I would use something like this. To obtain all combinations of locations-users you could use a Select query with no joins:
Select *
From Location, App_User
but from here you have to exclude combinations of locations-users that are included in user_location_list table. This can solve your problem:
Select
Location.*,
App_User.*
From App_USer, Location
Where
Not Exists (select * from user_location_exclusion
where user_location_exclusion.user_id=app_user.user_id
and user_location_exclusion.location_id=location.location_id)
This shows every location for every user that has not been excluded.
Related
I currently have this query set up:
SELECT age, hobbies, country
FROM profile
INNER JOIN matches
ON profiel.usernumber = matches.user1
WHERE age BETWEEN ? and ?
AND usernumber != ?
AND usernumber NOT IN (SELECT user2 FROM matches WHERE matches.user1 = ?)
FYI:
I'm using two tables.
Table "profile" has all the user info we need (age, hobbies, etc.).
Table "matches" has two user numbers of users that matched. Only if you click the "match" button you get inserted into the "matches" table with your user number and your match.
The problem:
The current query only selects users that are in the table "matches" but aren't matched with you. I also want the users that aren't matched at all (and thus aren't in the matches table)
I've tried multiple options with AND or OR but I haven't figured anything out myself. I'm new to coding so I'm grateful for any help.
INNER JOIN only show records that exists on both tables. Use LEFT JOIN instead.
SELECT age, hobbies, country
FROM profile
LEFT JOIN matches ON profiel.usernumber = matches.user1
I have a cross-reference table that supplies the many-to-many relationship between users and user group tables. It contains two relevant columns: group_id and user_id (surprise, surprise!). When a user wants to create a new group, I want to first check if that set of users already exists as a group.
Essentially I would define the problem as "Given a set of user ids, find any set of rows that match the set of user ids and all share the same group id".
Edit:
I'm looking for the exact set of users, not interesting in seeing in the resultset groups that include those users in addition to other users.
Sample Data
I have the hunch that a subquery is the way to go, but I can't figure out how to arrange it. Any help would be greatly appreciated!
Is this what you want?
select groupid
from usergroups ug
where userid in ($user1, $user2, . . . , $usern)
group by groupid
having count(*) = <n>;
This returns all groups that have the supplied list of users.
If you want the exact set, then:
select groupid
from usergroups ug
group by groupid
having count(*) = sum( userid in ($user1, $user2, . . . , $usern) );
This assumes that groups don't have the same user twice (it is not hard to adjust for that, but the condition becomes more complicated).
I've got a system on my website which is very similar to Facebook, where you can post statuses and your people can comment on your status, like it etc. This all gets inserted in the database in the following format, with child tables of the likes and comments with foreign keys set up in case the parent status gets deleted, the likes and comments get deleted with it.
I also have a friends table which contains the user ID of the user that started the friend request, the user ID of the user that has to either accept it or deny it, and the status of the record, whether it's accepted, denied or pending.
There's also a "users" table which contains the normal malarkey, such as emails, passwords etc. All records have a unique ID however, in the column "userID".
The query I have at the moment loads all statuses regardless of whether the status owner is your friend or not. The current query looks like this (I'm working in ColdFusion so ## are the variables passed to the function)
SELECT *,
(SELECT COUNT(*) FROM status_likes WHERE likeStatusID=statusID) AS StatusLikeCount,
(SELECT COUNT(*) FROM status_comments WHERE SID=statusID) AS StatusCommentCount
FROM status, users
WHERE statusOwner=userID
AND statusType='user'
ORDER BY statusDateTime DESC
LIMIT #args.indexStart#,#args.indexEnd#;
I need this query to only load statuses if the owner of the status is your friend. I can call a query to load a users friends and append a string containing the user ID's of all the friends, such as: "652,235,485,975" etc.
I tried doing an IN in the query so there was an extra line:
AND (statusOwner=#val(args.userID)# OR statusOwner IN (#usersFriendsString#))
However this brought back duplicate results and when I tried GROUP BY on the status owner, it didn't bring back records that it should have.
Any MySQL gurus out there able to help?
You should use something like that :
SELECT
s.*,
(SELECT COUNT(*) FROM status_likes WHERE likeStatusID=s.statusID) AS StatusLikeCount,
(SELECT COUNT(*) FROM status_comments WHERE SID=s.statusID) AS StatusCommentCount
FROM Users u
JOIN Friend f ON f.friendOwner = u.id
JOIN Status s ON s.statusOwner = f.id
WHERE u.id = <...>
ORDER BY s.statusDateTime DESC
You can use WHERE clause if you can't use a JOIN instruction.
Or you can use a IN instruction populated by a SELECT that retrieve all requiered status ids.
Two tables that log access to projects. I want to show the admin a list of projects/worksheets that he/she has not accessed since the user has last access that project/worksheet with an access_code of "e" (edited).
There may be a case where the admin has NEVER accessed that project/worksheet (i.e. no matching project/worksheet in admin table) (this is actually where I get stuck)
Obviously, what I was after is a single query that "does it all".. maybe dreaming..
admin_access
project_id, wksheet_id, id, access_date, access_code
user_access
project_id, wksheet_id, id, access_date, access_code
(where id is the user/admin id for that access event and access_date is a timestamp)
result table
project_id, wksheet_id
What I need is a list of records (project_id, wksheet_id) where the access_date in the table user_access is the greatest (i.e. admin has not access that project/worksheet since the user has last edited that worksheet. (note: the only other access_code is "v" for view))
It is not relevant that the admin had either viewed or edited that worksheet previously or which user had last access that record in the user_access table. (i.e. the id in the user_access table is not relevant)
I've gotten close but the killer seems to be where that admin does not have an access record in the admin_access table for that project. (max(date) returns NULL and then the comparison fails)
Looking for fresh angles on this..
I finally got something that works when the max(a.access_date) is NULL for the comparison
Select * from
(SELECT u.project_id, u.wksheet_id, max(u.access_date) as user_date, max(a.access_date) as admin_date FROM user_access u
left outer join admin_access a on (a.project_id = u.project_id and a.wksheet_id = u.wksheet_id)
where u.access_code = 'e'
group by u.project_id, u.wksheet_id) as maxtable
where maxtable.user_date > maxtable.admin_date or
maxtable.admin_date is NULL
The key was to take the query that produces a good list of possible matching project/worksheets and use that in a select where the comparison accounts for the possible null from the max(a.access_date) from the missing matching admin_access record.
It would be interesting to hear other ways to do this...
Please forgive my ignorance here. SQL is decidedly one of the biggest "gaps" in my education that I'm working on correcting, come October. Here's the scenario:
I have two tables in a DB that I need to access certain data from. One is users, and the other is conversation_log. The basic structure is outlined below:
users:
id (INT)
name (TXT)
conversation_log
userid (INT) // same value as id in users - actually the only field in this table I want to check
input (TXT)
response (TXT)
(note that I'm only listing the structure for the fields that are {or could be} relevant to the current challenge)
What I want to do is return a list of names from the users table that have at least one record in the conversation_log table. Currently, I'm doing this with two separate SQL statements, with the one that checks for records in conversation_log being called hundreds, if not thousands of times, once for each userid, just to see if records exist for that id.
Currently, the two SQL statements are as follows:
select id from users where 1; (gets the list of userid values for the next query)
select id from conversation_log where userid = $userId limit 1; (checks for existing records)
Right now I have 4,000+ users listed in the users table. I'm sure that you can imagine just how long this method takes. I know there's an easier, more efficient way to do this, but being self-taught, this is something that I have yet to learn. Any help would be greatly appreciated.
You have to do what is called a 'Join'. This, um, joins the rows of two tables together based on values they have in common.
See if this makes sense to you:
SELECT DISTINCT users.name
FROM users JOIN conversation_log ON users.id = converation_log.userid
Now JOIN by itself is an "inner join", which means that it will only return rows that both tables have in common. In other words, if a specific conversation_log.userid doesn't exist, it won't return any part of the row, user or conversation log, for that userid.
Also, +1 for having a clearly worded question : )
EDIT: I added a "DISTINCT", which means to filter out all of the duplicates. If a user appeared in more than one conversation_log row, and you didn't have DISTINCT, you would get the user's name more than once. This is because JOIN does a cartesian product, or does every possible combination of rows from each table that match your JOIN ON criteria.
Something like this:
SELECT *
FROM users
WHERE EXISTS (
SELECT *
FROM conversation_log
WHERE users.id = conversation_log.userid
)
In plain English: select every row from users, such that there is at least one row from conversation_log with the matching userid.
What you need to read is JOIN syntax.
SELECT count(*), users.name
FROM users left join conversion_log on users.id = conversation_log.userid
Group by users.name
You could add at the end if you wanted
HAVING count(*) > 0