How should joins used in mysql? - sql

If i have two tables like
user table-"u"
userid | name
1 | lenova
2 | acer
3 | hp
pass table-"p"
userid | password
1 | len123
2 | acer123
3 | hp123
as for as i learnt from tutorials I can join these 2 tables using many joins available in
mysql as said here
If i have a table like
role table-"r"
roleid | rname
1 | admin
2 | user
3 | dataanalyst
token table-"t"
tokenid| tname
1 | xxxx
2 | yyyy
3 | zzzz
tole_token_association table-"a"
roleid | tokenid
1 | 1
1 | 2
3 | 1
3 | 3
3 | 1
I have to make a join such that I have to display a table which corresponds
like this "rolename" has all these tokens.How to make this? I am confused. Is it possible to make a join? I am liking mysql a lot. I wish to play with queries such that not playing. I want to get well versed. Any Suggestions Please?

It's easiest to see when the column names that need to be joined are named identically:
SELECT r.rname,
t.tname
FROM ROLE r
JOIN ROLE_TOKEN_ASSOCIATION rta ON rta.roleid = r.roleid
JOIN TOKEN t ON t.tokenid = rta.tokenid
This will return only the roles with tokens associated. If you have a role that doesn't have a token associated, you need to use an OUTER join, like this:
SELECT r.rname,
t.tname
FROM ROLE r
LEFT JOIN ROLE_TOKEN_ASSOCIATION rta ON rta.roleid = r.roleid
JOIN TOKEN t ON t.tokenid = rta.tokenid
This link might help -- it's a visual representation of JOINs.

Related

SQL Query to show results that don't have a relation to variable

For an assignment I have which includes a delete and add friend system (like Facebook), I've made a query that works by using two SQL tables, one which includes a friend_id, name and other information, and another which holds two friend_id columns, that show the relationship with the users and if they're friends.
User Table (friends)
| friend_id | profile_name |
|:---------- |:------------:|
| 1 | John |
| 2 | Peter |
| 3 | Alex |
| 4 | Nick |
---------------------------
Friendship Table (myfriends)
| friend_id1 | friend_id2 |
|:---------- |:----------:|
| 1 | 3 |
| 2 | 4 |
| 3 | 1 |
| 4 | 2 |
-------------------------
I am wanting to get a query which selects people that don't have a connection with a result (I want to show anyone who doesn't have a connection to friend_id '1', so only want to show users 2 and 4), and then display their name.
I have a query that selects the ones which have the relation which is:
SELECT friends.profile_name,friends.friend_id FROM `myfriends` JOIN `friends` ON friends.friend_id = myfriends.friend_id2 WHERE `friend_id1` = 1;
The query bellow shows all results from the table, and even using '!=', it doesn't select those who don't have a relation to friend_id '1'
SELECT friends.profile_name,friends.friend_id FROM `myfriends` JOIN `friends` ON friends.friend_id = myfriends.friend_id2 WHERE `friend_id1` != 1;
How can I fix this query so it shows all results but those connected to ‘friend_id1’ = 1
with connected as (SELECT friend_id,
myfriends.friend_id2 friend
FROM myfriends
JOIN friends
ON friends.friend_id = myfriends.friend_id1
WHERE friend_id1 = 1)
select *
from friends
where friend_id not in (select distinct friend from connected union all select distinct friend_id from connected)
you cannot change the where clause as it specifies which user you want to focus on.
So first get the users that are connected (in the first cte), and then select all users except those found in the first result of the connected users.
By the way, your example is misleading as it can be solved with a bug by doing something simple in the join.
edit
while it wasn't clease which version you were using, (I thought with clause is available in the newer mysql versions) I created another solution that is working on mysql 5.6 and should work for you as well:
select f.*
from friends f
left join (
SELECT friend_id, myfriends.friend_id2 friend
FROM myfriends
JOIN friends
ON friends.friend_id = myfriends.friend_id1
WHERE 1 in (friend_id,friend_id2)) f1
on f1.friend = f.friend_id
where f1.friend is null
it has a nicer implementation in one part (1 in one of 2 columns), and uses a left join that takes the nulls from the right table.

How can I optimize a chat room query?

Background
I'm building a simple chat client for a custom web application. I am required to store all chat logs. Also users can message individuals or groups. Think google chat (which I told my client to use instead but he insisted on custom). My database is structured so:
Table: ChatRoom
int Primary Key ChatRoomID
varchar(64) Name
Table ChatMessage
int Primary Key ChatMessageID
int UserID
int ChatRoomID
varchar(2000) message
datetime date
Table ChatUser
int ChatRoomID
int UserID
int LastMessageID
Primary Key (ChatRoomID, UserID)
I am using SQL Server and will be migrating soon to mysql so the solution needs to work on both platforms.
My problem
Assuming a user has just logged in I need to pull a list of all chat rooms with outstanding messages. My current query looks like this:
SELECT DISTINCT
cr.ChatRoomID AS id,
cu.LastMessageID AS label
FROM ChatRooms cr
LEFT JOIN ChatUsers cu ON cu.ChatRoomID = cr.ChatRoomID
LEFT JOIN ChatMessages cm ON cm.ChatRoomID = cr.ChatRoomID
WHERE cu.UserID = :user_id
AND cu.LastMessageID < cm.ChatMessageID
The question
This seems to work rather well. However I suspect this will get inefficient when their are dozens of users, thousands of rooms, and millions of messages. How do optimize this query (or database structure) to make this request (number of chat rooms with outstanding messages for a given user) a performance scale-able query?
My primary concern is that I'm forced to use the "distinct" flag for this query. So this could be joining a temporary table to the millions before filtering down to 2 numbers.
Example data
Users
1 | Dr A
2 | Dr B
3 | Biller A
4 | Biller B
5 | Boss
ChatRoom
1 | Doctor Group
2 | Billing Group
ChatUser
Room | User | Message
- | - | -------
1 | 1 | 0
1 | 2 | 2
1 | 5 | 2
2 | 3 | 6
2 | 4 | 0
2 | 5 | 5
Chat Message
ID | Room | User | Message
- | - | - | -------
1 | 1 | 5 | "How is everybody today?"
2 | 1 | 2 | "I'm well. Need more band aids in room 5."
3 | 2 | 5 | "Can someone restock Room 5 with Band aids?"
4 | 2 | 3 | "That's not my job get a lackey."
5 | 2 | 5 | "Do it anyway or your fired."
6 | 2 | 3 | "It's your're not your and I quit."
In this scenario user 1 and 4 are late for work and when they log in a a message will pop-up, and user 5 is in for a surprise in his billing department next time I run the query.
You can optimize this query like this:
select cr.ChatRoomID AS id,
cu.LastMessageID AS label
from ChatUsers cu inner join ChatRooms cr ON cu.ChatRoomID = cr.ChatRoomID
where cu.UserID = :user_id and
exists (select 1 from ChatMessages cm where cm.ChatRoomID = cr.ChatRoomID and cu.LastMessageID < cm.ChatMessageID);
There are mainly 2 issues with your current query:
Left joining will also bring the blank records. Also there will be multiple records for a group which you are handling by using distinct.
The list of records are again joined will all message table data so if the message table will contain more data then you query is destined to get slow.
This is something similar we solved at https://www.applozic.com.
Disclaimer: I am working at Applozic.

Default values when NULL

related to this answered question I asked yesterday Recursive query where anchor and member have unions
I have a junction table that contains
menuItem | roleID | userID
1 | 2 | NULL
1 | 5 | NULL
2 | NULL | 81
I have a roles table
roleID | roleName
1 | admin
3 | super admin
5 | basic user
What I would like is when querying for the USERS who have access to this menu item to also include to the admin users (roleIDs 1 and 3). In the linked question I was able to include the admin users by including the IDs in the join clause. But because the roleID is null when including the individual users, I can't ever get the results to produce:
userName | menuItem
admin1 | 2
admin2 | 2
realUser | 2
At best i've been able to get
userName | menuItem
realUser | 2
realUser | 2
realUser | 2
How can I write this so the admin users are included as part of the result? thanks.
It sounds like you want to join a table Users to a table Menu
when
The user's Role ID is 1 or 3 and the menu role id is null
Or the userid in the user table matches the userid
Or the users's role id matches the menu's role id
So that looks like this
From
users u
INNER JOIN Menu m
ON (u.RoleID in (1,3) and m.roleID is Null)
or u.UserId = m.userId
or u.RoleId = m.RoleId

join on three tables? Error in phpMyAdmin

I'm trying to use a join on three tables query I found in another post (post #5 here). When I try to use this in the SQL tab of one of my tables in phpMyAdmin, it gives me an error:
#1066 - Not unique table/alias: 'm'
The exact query I'm trying to use is:
select r.*,m.SkuAbbr, v.VoucherNbr from arrc_RedeemActivity r, arrc_Merchant m, arrc_Voucher v
LEFT OUTER JOIN arrc_Merchant m ON (r.MerchantID = m.MerchantID)
LEFT OUTER JOIN arrc_Voucher v ON (r.VoucherID = v.VoucherID)
I'm not entirely certain it will do what I need it to do or that I'm using the right kind of join (my grasp of SQL is pretty limited at this point), but I was hoping to at least see what it produced.
(What I'm trying to do, if anyone cares to assist, is get all columns from arrc_RedeemActivity, plus SkuAbbr from arrc_Merchant where the merchant IDs match in those two tables, plus VoucherNbr from arrc_Voucher where VoucherIDs match in those two tables.)
Edited to add table samples
Table arrc_RedeemActivity
RedeemID | VoucherID | MerchantID | RedeemAmt
----------------------------------------------
1 | 2 | 3 | 25
2 | 6 | 5 | 50
Table arrc_Merchant
MerchantID | SkuAbbr
---------------------
3 | abc
5 | def
Table arrc_Voucher
VoucherID | VoucherNbr
-----------------------
2 | 12345
6 | 23456
So ideally, what I'd like to get back would be:
RedeemID | VoucherID | MerchantID | RedeemAmt | SkuAbbr | VoucherNbr
-----------------------------------------------------------------------
1 | 2 | 3 | 25 | abc | 12345
2 | 2 | 5 | 50 | def | 23456
The problem was you had duplicate table references - which would work, except for that this included table aliasing.
If you want to only see rows where there are supporting records in both tables, use:
SELECT r.*,
m.SkuAbbr,
v.VoucherNbr
FROM arrc_RedeemActivity r
JOIN arrc_Merchant m ON m.merchantid = r.merchantid
JOIN arrc_Voucher v ON v.voucherid = r.voucherid
This will show NULL for the m and v references that don't have a match based on the JOIN criteria:
SELECT r.*,
m.SkuAbbr,
v.VoucherNbr
FROM arrc_RedeemActivity r
LEFT JOIN arrc_Merchant m ON m.merchantid = r.merchantid
LEFT JOIN arrc_Voucher v ON v.voucherid = r.voucherid

One table, need multiple values from different rows/tuples

I have tables like:
'profile_values'
userID | fid | value
-------+---------+-------
1 | 3 | joe#gmail.com
1 | 45 | 203-234-2345
3 | 3 | jane#gmail.com
1 | 45 | 123-456-7890
And:
'users'
userID | name
-------+-------
1 | joe
2 | jane
3 | jake
I want to join them and have one row with two of the values like:
'profile_values'
userID | name | email | phone
-------+-------+----------------+--------------
1 | joe | joe#gmail.com | 203-234-2345
2 | jane | jane#gmail.com | 123-456-7890
I have solved it but it feels clumsy and I want to know if there is a better way to do it. Meaning solutions that are either more readable or faster(optimized) or simply best-practice.
Current solution: multiple tables selected, many conditional statements:
SELECT u.userID AS memberid,
u.name AS first_name,
pv1.value AS fname,
pv2.value as lname
FROM users AS u,
profile_values AS pv1,
profile_values AS pv2,
WHERE u.userID = pv1.userID
AND pv1.fid = 3
AND u.userID = pv2.userID
AND pv2.fid = 45;
Thanks for the help!
It's a typical pivot query:
SELECT u.userid,
u.name,
MAX(CASE WHEN pv.fid = 3 THEN pv.value ELSE NULL END) AS email,
MAX(CASE WHEN pv.fid = 45 THEN pv.value ELSE NULL END) AS phone,
FROM USERS u
JOIN PROFILE_VALUES pv ON pv.userid = u.userid
GROUP BY u.userid, u.name
Add "LEFT" before the "JOIN" if you want to see users who don't have any entries in the PROFILE_VALUES table.
I didn't say this, which I should have (#OMG Ponies) but I wanted to use this within a MySQL view. And for views to be editable you're not allowed to use aggregate functions and other constraints. So what I had to do whas use the same SQL query as I had described in the initial question.
Hope this helps someone, adding tag for creating views.