Help for a nested query - sql

my hosting company doesn't allow me to save query/view in mysql database (don't ask me ..why! i don't know), so i've to build an all in one nested query (that is difficult for my sql skills).
My app is a simple "ticket managment system"
Here the table structure ticket_master
ticket_id
assignedto_user
...
The other table, for users
userid
username
I've to get the count of ticket per user, starting from the USERS table to include ALL users (either who haven't ticket opened).
The result must be
userid, username, tot_ticket
1, mr a, 3
2, mr b, Null (or zero)
3, mr c, 2
4, mr d, Null (or zero)
...
Thanks in advance!

Select U.userid, U.username
, Count(TM.ticket_id) As tot_ticket
From users As U
Left Join ticket_master As TM
On U.userid = TM.assignedto_user
Group By U.userid, U.username

SELECT
userid,
username,
COUNT(ticket_id)
FROM users
LEFT OUTER JOIN ticket_master on users.userid = ticket_master.assignedto_user
Not much different than #Thomas answer, except that I would avoid the grouping if your user list is already unique.

Related

Beginner SQL question, trying to search for users who belong into same group

Tried to learn SQL like seven months ago and had to quit for a while. Came back to my baby level SQL course and tried to do the next lesson and got instantly jammed since I hardly remember what I have been taught before. I tried my best to read through my older lessons, but since these assignments are teaching how to apply your learnt skills (that were quite shaky from the start) I'm having hard time.
Assignment goes like this:
I have 3 tables with 2 columns:
[Users] [Groups] [Rights]
id name id gname user_id group_id
------------------ / ------------------------ / ---------------------------- /
1 Paul / 1 Ducks / 1 1 /
2 Jake / 2 Tadpoles / 1 2 /
3 Lilly / ------------------------ / 2 1 /
4 Anne / / 4 2 /
------------------ / /------------------------------/
I need to search for every user who belong to atleast one same group with Paul, and and print out their names.
I can manage to copy something like this from the assignment which came before this:
SELECT U.name
FROM Rights R
LEFT JOIN Groups G ON G.id = U.group_id
LEFT JOIN User U ON U.id = U.user_id
GROUP BY U.name
It magically produces
Paul
Jake
Anne
Which happens to be right answer, but if you change anything or add users/groups/rights it ofcourse stops working. (I'm not even quite sure why it is leaving poor Lilly out). Anyone who could push me to the right direction? I know that I should somehow probably use "HAVING X" where X is the list of group ids that Paul rocks.
P.S.: I'm VERY new to this matter and also English is my second language.
Start with one thing first... You are looking for PAUL, because you are looking for other users in the same group as Paul. Lets start there.
Select
u.id,
u.Name
from
User u
where
u.Name = 'Paul'
Now, you need to know which group(s) that Paul is in, so now you can associate that to the RIGHTS table on his ID only. I can also join to groups table to get the description. In this case Paul is associated with BOTH groups which will obviously pull in everyone since there are only two groups. However, you can at least see the steps from A -> B -> C for the relationships this far.
Select
u.id,
u.Name,
r.group_id,
g.gname
from
User u
JOIN Rights r
on u.id = r.user_id
JOIN Groups g
on r.group_id = g.id
where
u.Name = 'Paul'
Now, we have these rights for Paul. Now we can take this as a query in the FROM clause of an outer query to get other people that are NOT Paul
select
u2.id,
u2.Name
PaulsGroup.gname
from
( Select
u.id,
u.Name,
r.group_id,
g.gname
from
User u
JOIN Rights r
on u.id = r.user_id
JOIN Groups g
on r.group_id = g.id
where
u.Name = 'Paul' ) PaulsGroups
-- now, back to the groups table
JOIN Groups g2
-- joined on the same group Paul was in
on PaulsGroups.group_id = g2.group_id
-- and the user is NOT EQUAL (!=) to Paul as he is the basis of query
AND PaulsGroups.id != g.user_id
-- and from the second groups instance back to the users table
-- so we can get the OTHER person's name
JOIN Users u2
on g2.user_id = u2.id
order by
-- now we can put the order in the other person's name and then group name
u2.Name,
PaulsGroup.gname
Here you go. Basically you are going to use a sub query to select the group id (or group ids) that Paul belongs to. Then select all users that belong to a group id that Paul belongs to. It can be achieved with the following query.
select *
from Users u
join Rights gu
on u.id = gu.user_id
where gu.group_id in (select gu.group_id
from Users u
join Rights gu
on u.id = gu.user_id
where u.id = '1')
After sweating for one hour with all the info I gathered from here, I got this answer that works!
SELECT
U.name
-- I only need the name in this case.
FROM
(Select
R.group_id
FROM Users U
JOIN Rights R
ON U.id = R.user_id
JOIN Groups G
ON R.group_id = G.id
WHERE U.name = 'Paul' )UG
-- UG is now my table containing only the groups that Paul is in
JOIN Rights R ON R.group_id = UG.group_id
-- Comparing UG group_id:s to Rights group_id:s, filtering out every user_id that has same group_id with Paul.
JOIN Users U ON U.id = R.user_id
-- Fetching names for the user_id:s
GROUP BY U.name
-- Grouping up the names. Done!
This was right answer in my case since I had to include Pauls name in the list with the other names since technically Paul has same courses with Paul ;P.
While I got it finally done, this raised few questions. Why I didn't have to use the "Users U2" as DRapp had to do in his answer? Also I was wondering if its better to use WHERE as dporth did. (Tho this way I got bit more familiar with the JOIN)
For this requirement you don't need the table Groups at all, because you have the group ids in the table Rights (you don't need their names which is the only extra info that you would get from the table Groups).
You must join 2 copies of Users to 2 copies of Rights.
The 1st copy of Users will return the rows that you want and the 2nd is the one that will join to the row of 'Paul':
SELECT DISTINCT u1.*
FROM Users u1
INNER JOIN Rights r1 ON r1.user_id = u1.id
INNER JOIN Rights r2 ON r2.group_id = r1.group_id
INNER JOIN Users u2 ON u2.id = r2.user_id
WHERE u2.name = 'Paul' AND u1.name <> u2.name
The condition AND u1.name <> u2.name in the WHERE clause makes sure that only users other than 'Paul' will be returned. If you want 'Paul' also you can remove it.
I use DISTINCT because without it you may get the same user more than once.
See the demo.

SQL Sets (intersect, Union, And Not)

I have what appears to be a simple query, but is alluding my boolean challenged mind (not enough java (the liquid kind) today).
Three tables:
Users = (UserID, Username, Enabled, LoggedIn, SessionID, Email, SettingsTableVersion, FullName, Initials, UserData, InitialStatusID)
Groups = (GroupID, Groupname, Description, AutoAdd)
GroupMembers = (GroupID, UserID, ProjectID, IsMember)
I have a bunch of users and a dozen or so groups. I have a World group that has every User in it. I have a Terminated Users Group that has just 4 users in it.
What I want is a query that looks at World (everyone is in it) and takes out the Terminated User group users names. This yields me all active users! Blimey if this isn't causing me to pull my hair out. I would surmise its essentially World minus the intersection of World and Terminated Users. No luck thus far. SQL Server 2012.
TIA
A nested query?
Select *
from users
where userid not in ( select userid
from groupmembers
where groupid=[terminated])
Or is that too slow?
If I can assume
The groupnames are 'world' and 'terminatedusers'
duplicates do not exist in group members (groupID and userID are a Unique index)
you really mean World is ALL users an a terminated user coudnl't exist w/o being in the world group.
.
SELECT U.userID
FROM USERS U
INNER JOIN GROUPMEMBERS GM
on U.UserID = GM.UserID
INNER JOIN GROUPS G
on G.GroupID = GM.GroupID
WHERE GroupName in ('World', 'TerminatedUsers')
GROUP BY U.UserID
HAVING count(GM.GroupID) = 1
This basically find all the groups each user is in for the two groups. then only return those who are in just 1 group.
You could join to two defined sets as well:
SELECT U.userID
FROM Users U
INNER JOIN GroupMembers GMWorld
on U.userID = GMWorld.userID
and GMWorld.GroupID = [ID for world]
LEFT JOIN GroupMembers GMTerminated
on U.userID = GMTerminated.UserID
and GMTerminated.GroupID = [ID for terminated]
WHERE GMTerminated.userID is null
If you don't want to use the ID's you could left join twice to Group for GMterminated and GMWorld to use the names.
The 2nd query basically joins users to a worldset and a terminated set we keep all users in world except those having a record in the terminated set.
This one did the trick! Issue was I had one user that was deleted (flips the Enabled bit) and then re-added so they appeared in the list until I added the Enabled = 1. Thanks xQbert... again! Awesome stuff.
SELECt fullname
FROM Users U
LEFT JOIN GroupMembers GMWorld
on U.userID = GMWorld.userID
and GMWorld.GroupID = 3
LEFT JOIN GroupMembers GMTerminated
on U.userID = GMTerminated.UserID
and GMTerminated.GroupID = 14
WHERE GMTerminated.userID is null and Enabled = 1

SQL query to find out the number of tickets

I have two tables in MS Access.
Table 1: users
ID (auto int)
Name
Table 2: tickets
ID (auto int)
userName (int) (refers to ID in users table)
How can I list the user names and the number of tickets submitted?
This should give you the results that you want. You will want to use an aggregate function COUNT() and a GROUP BY
SELECT u.name, count(t.username) TicketsSubmitted
FROM Users u
INNER JOIN tickets t
ON u.id = t.username
GROUP BY u.name
Count the tickets per user in a subquery, then list all the info per user.
SELECT u.Name AS UserName, t.ticketCount AS TicketsSubmitted
FROM users AS u
INNER JOIN ( SELECT COUNT(ID), userName AS ticketCount FROM tickets GROUP BY userName ) AS t
ON u.ID = t.userName
Should do it.
A make table query should allow you to query those two tables and create a third with the data you want.
The following link from the Microsoft Office website should give you the information you need.
http://office.microsoft.com/en-us/access-help/create-a-make-table-query-HA010108505.aspx
The two answers above give solid advice for the query to get the data you want.

DISTINCT or GROUP BY used with NOT IN returns wrong results

I have two tables
users with columns: uid, name, mail, etc
users_roles with columns: uid, rid
In users_roles, each time a role is added to a user, it is listed in the users_roles table. So, let's say user 1 has roles 1 and 4. In the table:
users_roles:
uid | rid
1 | 1
1 | 4
I need to return all users who don't have roles 4 OR 5. I have tried using both Group By and Distinct combined with NOT IN. The problem I keep running into is if a user has both roles 1 and 4, they will be returned in the results. Below is a an example of my Group By query:
SELECT *
FROM users AS u
LEFT JOIN users_roles AS ur ON u.uid = ur.uid
WHERE ur.rid NOT
IN ( 4, 5 )
GROUP BY ur.uid
I have tried sub-queries as well to no avail because the issue seems to be that Group By combines rows after finishing the query. So, it simply finds the record containing uid 1 rid 4 and returns it in the results.
The Drupal module Views that I can't use (due to security issues with Views Bulk Operations) accomplishes the desired results by doing the following:
LEFT JOIN users_roles ON users.uid = users_roles.uid
AND (users_roles.rid = '4' OR users_roles.rid = '5')
For long term maintenance I don't want to have to update the code every single time we add a role and this is going to make for one long query.
I looked at the following:
Aggregating Rows
Filtering distinct rows in SQL
While there are Drupal functions that will let me get the list of role ids where I could unset the roles I don't want show up in the resulting array, I feel like I am missing a fundamental understanding of SQL. Is there a better way to do this in SQL?
I need to return all users who don't have roles 4 & 5
select *
from users u
where not exists
(
select *
from users_roles ur
where ur.rid in (4,5)
and ur.uid = u.uid
)
If you want to check for no existance of both 4 and 5 (and not neccessarily one of them), you can use
select *
from users u
where not exists
(
select uid
from users_roles ur
where ur.rid in (4,5)
and ur.uid = u.uid
group by uid having count(distinct rid)=2
)
If the list is long you can use a mapping table with all possible values and use that in the above query
I would like that :
SELECT *
FROM users AS u
LEFT JOIN users_roles AS ur ON (u.uid = ur.uid AND ur.rid IN ( 4, 5 ) )
WHERE ur.rid IS NULL
GROUP BY u.uid

SQL Query without nested queries

Let's say we have these tables;
table user:
- id
- username
- email
table user2group:
- userid
- groupid
table group:
- id
- groupname
How do I make one query that returns all users, and the groups they belong to (as an array in the resultset or something..)
select u.id, u.username, u.email, g.groupid, g.groupname
from user u
join user2group ug on u.userid=ug.userid
join group g on g.groupid=ug.groupid
order by u.userid
As you are looping through the result set, each time you see a new userid make a new user object (or whatever) and add the groups to it.
Eric's answer is great, but I would use a LEFT JOIN instead of an INNER to get users that do not belong to any group as well.
SELECT
u.id,
u.username,
u.email,
g.groupid,
g.groupname
FROM
user u
LEFT JOIN user2group ug ON u.userid = ug.userid
LEFT JOIN group g ON g.groupid = ug.groupid
ORDER BY
u.userid
Both of the above are more or less correct (deepends if each user has a group or not). But they will also both give a result set with several entries for each user.
There are ways of concatenating every group member into one comma separated string, I'd suggest you read about it here:
http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/
Another method I personally like is to use bit values instead of the relational table user2group
table user then gets a int (or bigint) field group, and each group ID is assigned one bit value (ie: 1,2,4,8,16 and so on) The value of the user table's group field is then the sum of the groupID it's assigned to. To query if its got a group you do:
where (group AND groupID = groupID)