I'm trying to perform what I assume is a very simple query on a MySQL DB. Here's my table setup;
Table 1 - CMS_AccessLevels
accessLevel
titleColor
Table 2 - CMS_Users
userID
username
userEmail
userAvatar
userSignature
accessLevel
I've already got this query;
SELECT `titleColor` FROM `CMS_AccessLevels` WHERE `accessLevel` = (SELECT `accessLevel` FROM `CMS_Users` WHERE `userID` = 3)
This works correctly and returns the correct titleColor value based on the accessLevel matching across both tables.
Now, what I want to do is also grab some of the values from CMS_Users as well. For the sake of simplicity, let's assume I want to grab only a few of the values, so my result set might look something like this;
userID|username|userAvatar|accessLevel|titleColor
-------------------------------------------------
0 |Scott |image.png | 6 |#FFFFFF
or as a PHP Array (shown just so you can see the logical layout if the above table didn't make sense);
array('userID' => $result['userID'],
'username' => $result['username'],
'userAvatar' => $result['userAvatar'],
'accessLevel' => $result['accessLevel'],
'titleColor' => $result['titleColor'];
Let's say I want to get userID, userName, userAvatar and accessLevel from CMS_Users, and titleColor from CMS_AccessLevels where CMS_Users.userID is equal to '3', remembering that CMS_AccessLevels.accessLevel and CMS_Users.accessLevel MUST match.
Realistically, the only piece of data I know before running the query is userID.
Is it possible to do this with a single query?
Try this:
SELECT u.userID, u.username, u.userAvatar, u.accessLevel, al.titleColor
FROM CMS_AccessLevels al
INNER JOIN CMS_Users u
ON u.accessLevel = al.accessLevel
WHERE u.userID = 3
You are using subqueries whereas joins will be the right choice. You might try something like
SELECT a.titleColor AS titleColor, u.username AS username FROM CMS_users u INNER JOIN CMS_AccessLevels a ON u.accessLevel = a.accessLevel WHERE u.userID = '3'
Related
I'm pretty basic at MySQL queries. I work on a Laravel web app, at this point it comes to edit the User Role's part. I have a table that lists options for a user's role (called mst_level), and another table reflects the list of users who has that role (called mst_user_level). Been search this topic several times, but always found a different case.
mst_level
mst_user_level
Expected Output:
Select all levels for a spesific user_id, and return all columns
in mst_level + 1 column called "checked", with the
condition if the user has that role in mst_user_level, return true,
else return false.
This is what I already did, which I know it's wrong
select a.*, coalesce(true, false) as checked from my_db_name.mst_level a
inner join my_db_name.mst_user_status b on a.id = b.mst_level_id
where b.mst_user_id = '363fdeea-5330-490a-b4db-34e32a3526d6'
Anyone can help me out with this? So much Appreciated...
You can do it with EXISTS:
SELECT m.*,
EXISTS (
SELECT 1
FROM my_db_name.mst_user_status s
WHERE s.mst_level_id = m.id
AND s.mst_user_id = '363fdeea-5330-490a-b4db-34e32a3526d6'
) AS checked
FROM my_db_name.mst_level m;
Or with a LEFT JOIN where the conditions are set in the ON clause:
SELECT m.*, s.mst_level_id IS NOT NULL AS checked
FROM my_db_name.mst_level m LEFT JOIN my_db_name.mst_user_status s
ON s.mst_level_id = m.id AND s.mst_user_id = '363fdeea-5330-490a-b4db-34e32a3526d6';
I am trying to check if the current user is already following the selected user, and I am doing this like so:
(I know it's not the best way, but as I am new to MYSQL this is as much as I have been able to come up with)
SELECT EXISTS (SELECT 1 FROM Activity WHERE IdOtherUser = 86 AND id = 145)
I am '145' and the user I selected is '86'.
Now that return '0' If I am not following and '1' If I am following that person.
Seems to be working already but it definetly needs improving!
Now what I would like to do is count the followers in the same query.
So count the people I am following and the people following me.
'Activity' is the table where I store the followers and I save them like this:
'id' = me
'idOtherUser' = other user I followed
'type' = type of action "follow"
I have done count's before when calculating the like counts, but I just cannot get my head around this!!
If anyone could spare some time to help me it is much appreciated!
I am sorry if the question is not the best, but I am still learning and trying my best to format them as clear as possible to understand.
Thanks in advance!!
If you trying to count the followers from specific id from table Activity you might do this way:
SELECT COUNT(idOtherUser) AS "I Follow",
(SELECT COUNT(idOtherUser) FROM Activity WHERE idOtherUser = 145 AND type = "follow"
) AS "FOLLOW ME",
(SELECT COALESCE(id,0) FROM Activity WHERE IdOtherUser = 86 AND id = 145 AND type = "follow")
FROM Activity WHERE id = 145 AND type = "follow"
you can use a "correlated subquery" to simplify the query and you might want distinct in the count also (depends on you data). I would avoid using spaces in column aliases too.
SELECT
COUNT(DISTINCT A1.idOtherUser) as i_follow
, (
SELECT
COUNT(DISTINCT A2.id)
FROM Activity A2
WHERE A2.idOtherUser = A1.id
AND A2.type = 'follow'
) as following_me
FROM Activity A1
WHERE A1.id = 145
AND A1.idOtherUser = 86
AND A1.type = 'follow'
Try it with distinct then without, if the result is the same leave distinct out of the query.
Say I have the following tables
User
__________
id
username
email
FriendGame
__________
id
useroneid
usertwoid
status
I want to get games that the current user is part of, so I do this:
SELECT *
FROM FriendGame
WHERE useroneid=1663702020516206
OR usertwoid=1663702020516206
AND STATUS =1;
This is fine. Now I want to join the username, but only for the user that ISNT the supplied user (1663702020516206) since in FriendGame the given user exists EITHER as useroneid or usertwoid.
You can pretty much translate your logic directly into an on clause:
SELECT fg.*
FROM FriendGame fg JOIN
User u
ON (fg.useroneid = 1663702020516206 and fg.usertwoid = u.id) or
(fg.usertwoid = 1663702020516206 and fg.useroneid = u.id)
WHERE 1663702020516206 in (fg.useroneid, fg.usertwoid) AND
STATUS = 1;
Actually, the where clause is not necessary to get the right result set, but I think it makes the intention of the query clearer.
dont blame for the database design.I am not its database architect. I am the one who has to use it in current situation
I hope this will be understandable.
I have 3 tables containing following data with no foreign key relationship b/w them:
groups
groupId groupName
1 Admin
2 Editor
3 Subscriber
preveleges
groupId roles
1 1,2
2 2,3
3 1
roles
roleId roleTitle
1 add
2 edit
Query:
SELECT roles
from groups
LEFT JOIN preveleges ON (groups.groupId=preveleges.groupId)
returns specific result i.e roles.
Problem: I wanted to show roleTitle instead of roles in the above query.
I am confused how to relate table roles with this query and returns required result
I know it is feasible with coding but i want in SQL.Any suggestion will be appreciated.
SELECT g.groupName,
GROUP_CONCAT(r.roleTitle
ORDER BY FIND_IN_SET(r.roleId, p.roles))
AS RoleTitles
FROM groups AS g
LEFT JOIN preveleges AS p
ON g.groupId = p.groupId
LEFT JOIN roles AS r
ON FIND_IN_SET(r.roleId, p.roles)
GROUP BY g.groupName ;
Tested at: SQL-FIDDLE
I would change the data structure it self. Since It's not normalised, there are multiple elements in a single column.
But it is possible with SQL, if for some (valid) reason you can't change the DB.
A simple "static" solution:
SELECT REPLACE(REPLACE(roles, '1', 'add'), '2', 'edit') from groups
LEFT JOIN preveleges ON(groups.groupId=preveleges.groupId)
A more complex but still ugly solution:
CREATE FUNCTION ReplaceRoleIDWithName (#StringIds VARCHAR(50))
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE #RoleNames VARCHAR(50)
SET #RoleNames = #StringIds
SELECT #RoleNames = REPLACE(#RoleNames, CAST(RoleId AS VARCHAR(50)), roleTitle)
FROM roles
RETURN #RoleNames
END
And then use the function in the query
SELECT ReplaceRoleIDWithName(roles) from groups
LEFT JOIN preveleges ON(groups.groupId=preveleges.groupId)
It is possible without function, but this is more readable. Made without editor so it's not tested in anyway.
You also tagged the question with PostgreSQL and it's actually quite easy with Postgres to work around this broken design:
SELECT grp.groupname, r.roletitle
FROM groups grp
join (
select groupid, cast(regexp_split_to_table(roles, ',') as integer) as role_id
from privileges
) as privs on privs.groupid = grp.groupid
join roles r on r.roleid = privs.role_id;
SQLFiddle: http://sqlfiddle.com/#!12/5e87b/1
(Note that I changed the incorrectly spelled name preveleges to the correct spelling privileges)
But you should really, really re-design your data model!
Fixing your design also enables you to define foreign key constraints and validate the input. In your current model, the application would probably break (just as my query would), if someone inserted the value 'one,two,three' into the roles table.
Edit
To complete the picture, using Postgres's array handling the above could be slightly simplified using a similar approach as MySQL's find_in_set()
select grp.groupname, r.roletitle
from groups grp
join privileges privs on grp.groupid = privs.groupid
join roles r on r.roleid::text = any (string_to_array(privs.roles, ','))
In both cases if all role titles should be shown as a comma separated list, the string_agg() function could be used (which is equivalent to MySQL's group_concat()
select grp.groupname, string_agg(r.roletitle, ',')
from groups grp
join privileges privs on grp.groupid = privs.groupid
join roles r on r.roleid::text = any (string_to_array(privs.roles, ','))
group by grp.groupname
Here i posted a question about doing JOIN depending on the value of the column in the row.
You will need that is you make an Add Friend feature, where you write the ID of the 2 users who are Adding each other into user_1_id (my id) and user_2_id (friend id).
When you want to see who you are friends with, select DEPENDING on whether user_1_id or user_2_id has the ID of the surrent User (the one who is browsing)
I figured it out so down below is the query you want to use in case you need to do it.
Here is the query
$sql_inp = 'SELECT DISTINCT
users.id, users.first_name, users.last_name,
CASE
WHEN friends.user_2_id="'.$_SESSION[USER][id].'" //equal to current user id
THEN (SELECT friends.user_1_id FROM friends WHERE friends.user_2_id="'.$_SESSION[USER][id].'") // if user_2_id is My id, then fetch the other row
ELSE friends.user_2_id // obviously the opposite
END
FROM users
LEFT JOIN friends ON users.id= // this case is completely the same as one above
CASE
WHEN friends.user_2_id="'.$_SESSION[USER][id].'" THEN (SELECT friends.user_1_id FROM friends WHERE friends.user_2_id="'.$_SESSION[USER][id].'")
ELSE friends.user_2_id
END
WHERE friends.user_1_id="'.$_SESSION[USER][id].'" OR friends.user_2_id="'.$_SESSION[USER][id].'" // fetch the row where the either one of the values is equal to My id
';
Hope this helps if anyone had trouble
Personally, I hate using CASE statement. It makes queries look cluttered. Try using the IF function
$sql_inp = 'SELECT
table1.val1,table1.val2,
table2.val1,table2.val2,
IF(table3.val1="'.$user_id.'",table3.val1,
IF(table3.val2 ="'.$user_id.'",table3.val2,
IFNULL(table3.val2,-1))) users_fetch
FROM table1
INNER JOIN table2 ON table2.val1=table1.val1
LEFT JOIN table3 ON table2.val1=users_fetch';
In this query, if table3.val1 and table3.val2 <> $user_id, then users_fetch is -1.
Give it a Try !!!