To read and output information about our network license I run a script every few minutes which calls a command line utility and dump the information in 3 mysql tables. Now I would like to get the active sessions and group name where the user belongs to (if the user is member of a group).
The 3 tables I use are:
Table: lm_session
Featureid (holds the feature id the session is connected to)
Username (the person using the license)
Workstation (system connected to the license)
In (datetime of starting the software)
Out (datetime of closing the software)
Key ( session id)
Active (yes/no)
Table lm_group
Featureid (holds the feature id the group is connected to
Name (name of the group)
License (number of licenses for the group)
Table lm_group_user
Group_id (Group Id the user is connected to)
Feature_id (Feature id *probably unnecessary)
Username (username)
At this moment I have a query like this
SELECT s.*, g.* FROM lm_session AS s
LEFT JOIN lm_group_user AS u ON s.user = u.user
LEFT JOIN lm_group AS g ON g.id = u.group_id
WHERE s.feature_id = :featureid AND s.active = 1
I do get all the users that having an active session, but the name of the group is not found properly. If a user is member of multiple groups, the s.user = u.user only finds the first group. The result is not based on a combination of s.user = u.user AND g.id = u.group_id
Can anybody help me?
i have found error in your sql query in first line:
SELECT s., g. FROM lm_session AS s ( not session )
since you are using s as alias in rest of query, but in first line you defined session as alias so that was wrong. try only s as alias in first line.
cheers.
Try to run the query below:
SELECT
s.Username,
s.Featuredid,
g.Name AS groupname
FROM lm_session s
JOIN lm_group_user gu ON s.Username = gu.Username
LEFT JOIN lm_group g ON s.Featuredid = g.Featuredid
WHERE s.Active = 1
Optional:
Add
GROUP BY a.Username
(If an user have multiple session in the lm_session table you might want to add this line to avoid duplicates, but you can start running this query without it see what happens. Since I was thinking an user might have multiple session in different software )
Related
I manage a property website. I have a table with banned users (small table) and a table called advert_views which keeps track of each listing that each user views (currently 1.3m lines and growing). The advert_views table alsio takes note of the IP address for every advert viewed).
I want to get the IP addresses used by the banned users and check if any of these banned users have opened new accounts. I ran the following query:
SELECT adviews.user_id AS 'banned user_id',
adviews.client_ip AS 'IPs used by banned users',
adviews2.user_id AS 'banned users that opened a new account'
FROM banned_users
LEFT JOIN users on users.email_address = banned_users.email_address #since I don't store the user_id in banned_users
LEFT JOIN advert_views adviews ON adviews.user_id = users.id AND adviews.user_id IS NOT NULL # users may view listings when not logged in but they have restricted access to the information on the listing
LEFT JOIN (SELECT client_ip,
user_id
FROM advert_views
WHERE user_id IS NOT NULL
) adviews2
ON adviews2.client_ip = adviews.client_ip
WHERE banned_users.rec_status = 1 and adviews.user_id <> adviews2.user_id
GROUP BY adviews2.user_id
I applied an index on the advert_views table and the users table as per below:
enter image description here
My query takes half an hour to execute. Is there a way how to improve my query speed?
Thanks!
Chris
First of all: Why do you outer join the tables? Or better: Why do you try to outer join the tables? A left join is meant to get data from a table even when there is no match. But then your results could contain rows with all values null. (That doesn't happen though, because adviews.user_id <> adviews2.user_id in your where clause dismisses all outer-joined rows.) Don't give the DBMS more work to do than necessary. If you want inner joins, then don't outer join. (Though the difference in execution time won't be huge.)
Next: You select from banned_users, but you only use it to check existence. You shouldn't do this. Use an EXISTS or IN clause instead. (This is mainly for readability and in order not to produce duplicate results. This probably won't speed things up.)
SELECT av1.user_id AS 'banned user_id',
av2.client_ip AS 'IPs used by banned users',
av2.user_id AS 'banned users that opened a new account'
FROM adviews av1
JOIN adviews av2 ON av2.client_ip = av1.client_ip AND av2.user_id <> av1.user_id
WHERE av1.user_id IN
(
SELECT user_id
FROM users
WHERE email_address IN (select email_address from banned_users where rec_status = 1)
)
GROUP BY av2.user_id;
You may replace the inner IN clause with a join. It's mostly a matter of personal preference, but it is also that in the past MySQL sometimes didn't perform well on IN clauses, so many people made it a habit to join instead.
WHERE av1.user_id IN
(
SELECT u.user_id
FROM users u
JOIN banned_users bu ON bu.email_address = u.email_address
WHERE bu.rec_status = 1
)
At last consider removing the GROUP BY clause. It reduces your results to one row per reusing user_id, showing one of its related banned user_ids (arbitrarily chosen in case there is more than one). I don't know your tables. Are you getting many records per reusing user_id? If not, remove the clause.
As to indexes I suggest:
banned_users(rec_status, email_address)
users(email_address, user_id)
adviews(user_id, client_ip)
adviews(client_ip, user_id)
i'm developing a user permission system, here is my eer
each user can have permission if:
he is a member of a group and that group have permission
he have record in user_permission table
so to get all permission that a user have, i must get a union of group permission that the user belong to, and the permission the user have in user_permission table
that the sql statement that I found as solution, but I don't know if it is the good one, (can I have the result using only one SELECT statement)
select statement:
SELECT p.name FROM permission p
JOIN group_permission as gp ON gp.permission_id = p.id
JOIN `group` as g ON g.id = gp.group_id
JOIN `user_group` as ug ON ug.group_id = g.id
where ug.user_id = 5
UNION
SELECT p.name FROM permission p
JOIN `user_permission` as up ON up.permission_id = p.id
where up.user_id = 5
here is the mysql dump of my database:
https://pastebin.com/2Kaq8fVs
The 3rd line is not necessary. You can join gp and ug via group_id. Keep your group table for securing foreign keys, but you don't need it for this kind of query. Everything else is OK for your requirement as you described it.
Note: Reading all permissions without regularly reloading them causes changed permissions not to be recognized by your application. If this is a problem, you could additionally constrain your query with a specific permission.
Here's one example, I have a Car, User, Member, and Dealer tables. At the moment I'm trying to get the name of a dealer who owns a car by matching their userids up
Select userid, name FROM `User` WHERE userid IN
(SELECT userid FROM 'Car' WHERE userid in
(SELECT userid FROM `Member` WHERE userid IN
(SELECT userid FROM `Dealer`)))
This does what I want but I can't help feel there's a better way of doing it? Currently the userid in Car is a FK of the userid in Dealer which is a FK of the userid in Member which is a FK of the userid in User which stores the name.
Can I go straight to getting all the userid's and names of dealers who's id is in the Car table, whilst making sure they're actually a Dealer?
Basically your schema is a downstream schema
Users -> Members -> Dealer -> Car
Good thing is you made all the possible keys that you need here.
So to selct anything in any table just go down stream from Users for example for the data you want
Select * from `USER` u
where
dealer.user_id = car.user_id and
member.user_id = dealer.user_id and
u.user_id = member.user_id
The reason i went upstream in matching records is because we want to make as few matching operations as possible. As you can see user table is supposed to contain the maximum records. So i match with car table and get all the user_id where there is a match in dealer. similarly i go from dealer to member and then to user. this means all the records of users will be matched with a lot fewer records that they would have been if i went from users to members to dealer to car.
But this is not fool proof solution. it will depend on your data. because it may be a case where one user may have multiple cars, then it would be better to go downstream.
Use JOIN instead of subqueries to fetch the data.
Try this:
SELECT U.userid, U.NAME
FROM `User` U
INNER JOIN Car C ON U.userid = C.userid
INNER JOIN Member M ON C.userid = M.userid
INNER JOIN Dealer D ON M.userid = D.userid;
First of all, let me explain my data structure.
I have a model called profile, witch is a profile for a service provider. The service provider may choose from two diferent types of "travels" (both and none of them also). The first travel mode is "I can go to the client". For that, the service provider needs to input all cities he can travel for (many-to-many). The second "travel" mode is "My client can come to me". Those, he chooses the city he is placed from a list (one-to-many).
For the "I can go to the client" travel mode, I have a locals_profile table with a profile_id and a local_id and a locals table with the city name.
For the "My client can come to me", I have a locations table with profile_id and city_id and a cities table with the name of the city.
Before you ask, because of other things in the project modeling, I couldn't use the same table for the city name in both cases (if I could, the performance would be increased?).
Also, the profile belongs to many sub_categories, witch bring us another table called profiles_sub_categories with sub_category_id and profile_id field.
What I want is, given a sub_category, show how many items I have in each city. Eg:
For the "designers" sub category:
New york (100)
San Fracisco (50)
Miami (10)
... (max 10 cities)
I've acomplished what I wanted with the following query:
select q1.city_name, if(q1.city_count is null, 0, q1.city_count) + if(q2.city_count is null, 0, q2.city_count) city_count
from
(select l.name as city_name, count(*) as city_count from locals_profiles lp
inner join locals l on l.id = lp.local_id
inner join profiles p on p.id = lp.profile_id
inner join profiles_sub_categories ps on ps.profile_id = p.id
where ps.sub_category_id = 97 and l.level = 2
group by 1) q1
left join
(select c.name as city_name, count(*) as city_count from cities c
inner join locations lo on lo.city_id = c.id
inner join profiles p on lo.profile_id = p.id
inner join profiles_sub_categories ps on ps.profile_id = p.id
where ps.sub_category_id = 97
group by 1) q2
on q1.city_name = q2.city_name
order by 2 DESC
limit 10;
But the query is taking too long to be executed. Since it's a web application, I need it to be almost instantly. Does anyone knows a better way to do what I'm trying?
I've found mysql doesn't do a great job of optimizing queries. The easy solution is writing two queries and merging them in the application layer. Suprisingly, it will probably be faster...
Alternatively, you could try eliminating the views (q1,q2) and aliasing the same tables multiple times instead. Might be tough to get right....
I have a query where I currently get information from 2 tables like this:
SELECT g.name
FROM site_access b
JOIN groups g
ON b.group_id = g.id
WHERE b.site_id = 1
ORDER BY g.status ASC
Now I wanted to have another table with this query but this one table would return more then 1 row is that possible at all ?
All I could make was it pull 1 row from that table, the field I want is a string field and it is ok to join the result with a separator too as long as all the matchs can be pulled together in this query.
If you need more information about the tables or anything feel free to say I didnt think it would be needed as this is mostly an example of how to pull multiple rows from a join/select query.
UPDATE of what the above query would result:
Admin
Member
Banned
Now with my 3rd table each access have commands they are allowed to use so this 3rd table would list what commands each one has access to, example:
Admin - add, del, announce
Member - find
Banned - none
UPDATE2:
site_access
site_id
group_id
groups
id
name
status
groups_commands
group_id
command_id
commands
id
name
SELECT g.name, GROUP_CONCAT(c.command) AS commands
FROM site_access b
JOIN groups g
ON b.group_id = g.id
JOIN groups_commands gc
ON g.id = gc.group_id
JOIN commands c
ON gc.command_id = c.id
WHERE b.site_id = 1
GROUP BY g.name
ORDER BY g.status ASC