first of all sorry for my bad english. The scenario i have is the following:
I am developing a notification services that sends notifications messages to many users. I have the following 3 tables on MySql
users(user_id)
notifications(notification_id, notification)
notifications_log(notification_log_id, notification_id, user_id)
Every time that a user read a notification, i insert a record on notifications_log table, ex. John user with user_id = 2 read the notification with notification_id =3: "This is a notification", and then i insert a record on notifications_log with user_id = 2 and notification_id = 3.
There is all ok, but i have to create a query to get all the notifications for all the users that not are inserted on notifications_log. What i have is:
SELECT u.user_id, n.notification_id, n.notification, nl.notification_log_id
FROM users as u
LEFT JOIN notifications_log as nl ON nl.user_id = u.user_id
CROSS JOIN notifications as n
WHERE u.user_id NOT IN (SELECT nl.user_id FROM notifications_log as nl)
AND u.user_id = 1 /* test with user 1 */
If there is no records on notifications_log table of user 1, query results show me
user_id | notification | notification_id | notification_log_id
------------------------------------------------------------------------------
- 1 | Notification_1 | 1 | null
- 1 | Notification_2 | 2 | null
But if i insert at least 1 record on notifications_log for user and notification_2, then i get empty results, and i should get:
user_id | notification | notification_id | notification_log_id
----------------------------------------------------------------------------
- 1 | Notification_1 | 1 | null
It seems that the query joins the notification_log_id to the other record with null notification_log_id...
In short, what I need it is get all the notifications from a especific user that there are not on inserted on the table notifications_log
Thanks in advance!
The query you want is probably this one:
select n.notification_id, u.user_id
from notifications n
cross join users u
left join notifications_log nl
on n.notification_id = nl.notification_id
and nl.user_id = u.user_id
where nl.notification_log_id is null
demo here
This query eliminates your derived table, reducing the execution time, and performs the cross join as early as possible to reduce the total number of rows being operating on.
But i'd suggest rethinking this altogether. Once notifications and users table reaches critical mass this is going to create millions upon millions of rows to filter.
A better idea would be to have a notification_inbox table, as a counterpart to your notifications_log table. When a notification is created, place it in the inbox table for each user. That way you can perform a simple query on a single table to determine unread notifications per user, rather than a potentially horrendously performing cross join.
Alternatively again, a single notification_delivery table, rather than inbox and log tables, which has a 'read' flag. This would also allow targeted notifications, as well as bulk delivery to all users.
It seems like you are on the right track but should just be changing user_id to notification_id in the 2nd to last line:
SELECT u.user_id, n.notification_id, n.notification, nl.notification_log_id
FROM users as u
LEFT JOIN notifications_log as nl ON nl.user_id = u.user_id
CROSS JOIN notifications as n
WHERE n.notification_id NOT IN (SELECT nl.notification_id FROM notifications_log as nl)
AND u.user_id = 1 /* test with user 1 */
Related
I have two tables in my MySql database:
user
- sid
- userid
- username
log
- sid
- userid
- login_time
As you can guess, there's a lot more records in log tables than in user table.
I am using php to present these records on my website in a table format as shown below.
no | userid | username | number of login |
1 | inzo | harvey | 233 |
2 | chae | schmidts | 433 |
3 | hibro | swainy | 12 |
To get the number of login for each user, I can send another queries in a for statement. But it's consuming resources and making the server slow in the end.
Can I have this result in one single join query?
Yes you can, you have to use count the logins for each user with a group by
select t1.userid, t1.username, count(t2.sid)
from user t1
left join
log t2
on t1.userid = t2.userid
group by t1.userid, t1.username
The left join ensures you that users without logins will still be returned, wit 0 as count.
Edit
About the question in the comment: if you want to only count the logins with a specific flag value, you can just add where flag = x before the group by; if you want to have a separate count for each value of the flag, you have to add that flag to both group by and select.
I guess best and by that I mean least resource consuming way would be to add "number_of_login" to user table and just increase it every time he/she is logged in, because any other solution will require looping
SELECT TABLE_A.row_id, TABLE_A.category, TABLE_A.val_1, TABLE_B.val_2
FROM TABLE_B
LEFT OUTER JOIN TABLE_A ON TABLE_B.row_id = TABLE_A.row_id
ORDER BY row_id;
If you want all the results, you need an outer join, not an inner one.
select a.sid,a.userid,a.username,COUNT(b.sid) from user a
Left join log b ON b.sid =a.sid group by a.sid
I'm trying to get a list of customers waiting on service and idle agents based on the following tables:
Customers Table
|Customers_ID||Customer Name|
|1 ||John |
|2 ||Sam |
|3 ||Kuji |
Agents Table
|AGENT_ID||AGENT Name|
|99 ||Kelly |
|98 ||Raji |
|97 ||Mertle |
Service Table (Customers being served by Agents)
|QUE_ID||AGENT Name||Customer|
|1001 ||Kelly ||Kuji |
|1002 ||Raji ||Sam |
SELECT Customer.custname
FROM Customer LEFT JOIN Service ON ( Customer.custname = Service.custname)
UNION
SELECT Agent.agentname
FROM Agent RIGHT JOIN Service ON ( Agent.agentname = Service.agentname)
WHERE
(Agent.agentname = service.agentname) IS NULL
GROUP BY Customer.custname";
This is returning all the values in the customer table and not the customers without agents. How can I exclude the records that already have a match?
You can use NOT EXISTS to check for agents and customers that are not yet in Service table:
SELECT custname
FROM Customer c
WHERE NOT EXISTS(
SELECT 1
FROM Service s
WHERE s.custname = c.custname
)
UNION ALL
SELECT agentname
FROM Agent a
WHERE NOT EXISTS(
SELECT 1
FROM Service s
WHERE s.agentname = a.agentname
)
It looks like you are trying to do a full outer join. While MySQL does not have a FULL OUTER JOIN keyword, you can simulate it by taking the union of two left joins. The left joins in the query below give the number of idle customers and agents. The critical piece is the WHERE ... IS NULL condition, which will match a customer or agent not already assigned.
SELECT c.custname
FROM Customer c LEFT JOIN Service s
ON c.custname = s.custname
WHERE s.custname IS NULL
UNION ALL
SELECT a.agentname
FROM Agent a LEFT JOIN Service s
ON a.agentname = s.agentname
WHERE s.agentname IS NULL
LEFT or RIGHT JOIN will ensure to return all records with none or some columns having NULL values (in the final table).
You can use NOT EXISTS as per the following (that will reduce the number of records, instead of all):
SELECT custname
FROM Customer
WHERE NOT EXISTS (SELECT custname
FROM Service
WHERE Customer.custname = Service.custname)
UNION ALL
SELECT agentname
FROM Agent
WHERE NOT EXISTS(SELECT agentname
FROM Service s
WHERE s.agentname = a.agentname)
So, lets say i have a user table. Each user has the ability to be in a team with upto 3 other users. So for now i have a column for each spot in the team(4 columns total, so your own id fills in a spot so you know where you fit in the team). And i put the ids to the other members of the team in each of the other columns. In the end, everyone on one team would have the same values in those 4 columns.
How would i query sql to look at those ids and pull the info for all the other users on there team (so by looking at one user, i can pull all 4 team members rows)? Is this the most efficient way of storing that data?
Normalize your data from the beginning. It will pay off big time in a long run. This way you'll be able to normally maintain and query your data.
A proposed schema in a simplified form may look like this
CREATE TABLE users
(
`user_id` int not null auto_increment primary key,
`user_name` varchar(5)
);
CREATE TABLE teams
(
`team_id` int not null auto_increment primary key,
`team_name` varchar(5)
);
CREATE TABLE team_users
(
`team_id` int,
`user_id` int,
primary key (team_id, user_id),
foreign key (team_id) references teams (team_id),
foreign key (user_id) references users (user_id)
);
If you need to pull all members for a team with a name 'team2'
SELECT t.team_id, t.team_name, u.user_id, u.user_name
FROM team_users tu JOIN teams t
ON tu.team_id = t.team_id JOIN users u
ON tu.user_id = u.user_id
WHERE t.team_name = 'team2'
If you need to get all members of a team where user with user_id = 2 is a member
SELECT t.team_id, t.team_name, u.user_id, u.user_name
FROM team_users tu JOIN team_users tu2
ON tu.team_id = tu2.team_id JOIN teams t
ON tu.team_id = t.team_id JOIN users u
ON tu.user_id = u.user_id
WHERE tu2.user_id = 2
Sample output:
| TEAM_ID | TEAM_NAME | USER_ID | USER_NAME |
|---------|-----------|---------|-----------|
| 2 | team2 | 2 | user2 |
| 2 | team2 | 4 | user4 |
| 2 | team2 | 5 | user5 |
Here is SQLFiddle demo
I think first of all you should not care about this being the most efficient way of storing such data, but the most logical. MySQL is generally pretty good at being a relational database, so the following should perform extremely well:
Make two tables. One for users (with IDs), one for the teams.
In the team table, you put the 4 IDs of the users. You can put in a team ID and name or whatever if you like, but don't have to.
Then you find the team entry like this:
SELECT * FROM team WHERE u1 == ? OR u2 == ? OR u3 == ? or u4 == ?;
And then you query for the users separately.
To improve performance, you may then think about table joins, joining the user's data onto the team entry:
SELECT * from team
LEFT JOIN user user1 ON u1 == user1.id
LEFT JOIN user user2 ON u2 == user2.id
LEFT JOIN user user3 ON u3 == user3.id
LEFT JOIN user user4 ON u4 == user4.id;
This will fetch you one row per team with all user details in it.
Even better: many to many
A many to many relationship has two tables (users and teams) and a relation table (team_users), that contains pairs of IDs and potential other values (e.g. position on the team).
Then you can map the user to his team, and get all the users (and additional values) from that, all using only the relation table. Using joins, you can again fetch your information along with the mapping, reducing the number of queries. MySQL is really good at this!
I have two tables, one table is called queuelist and the other is call info. In the queuelist table it just lists different IDs. I am trying to get the 'clientID' from that table and match it with the 'ID' in the other table that contains all of the info and display it back on the page. Here is how the tables look:
Table - queuelist
ID | clientID
-------------
1 | 589
2 | 254
3 | 486
Table - info
ID | Name | Phone
--------------------
256 | Bob | 5551231234
486 | Jack | 5551231234
589 | Jill | 5551231234
This is what they call joining tables, you should use a query like this:
SELECT i.ID, i.Name, i.Phone FROM `queuelist` AS q
LEFT JOIN `info` AS i ON (
q.clientID = i.ID
);
I'm using aliases for shorter notation in the above query (queuelist becomes q and info becomes i) and then set the join condition (the bit between the ON()) to be the clientID from the queuelist table should match the ID in the info table.
Also see http://dev.mysql.com/doc/refman/5.0/en/join.html for more details.
You need to use an inner join
select * from queuelist as ql inner join info as i on ql.clientID = i.ID
Though you might want to replace * with specific field names e.g
select ql.clientID, i.fieldname FROM....
Well, I see no difficulty in this using a JOIN.
SELECT * FROM queuelist JOIN info ON clientID = info.ID WHERE queuelist.ID = 2
"Where" would be another option.
SELECT Name, Phone FROM queuelist,info WHERE clientID = ID
Assuming you want only name and phone
I have many a times tried using nested query for MySQL in PHP, but it does not work. Is it not possible to do nested/Joins queries?
Just a Scenario:
I have two tables one table with user id and the other with data. User logins and with sessions I have to cross check two different tables with user id (user and data). Is it not possible to nest/join these two tables to write a single query statement.
In short is nesting or joining two or more tables permitted in PHP coding?
YES, it is possible to join two or more tables in MySQL (and therefore, also when using PHP).
You need to post your table schema, if you want us to show a relevant join query. You could, however, try something like:
SELECT * FROM user AS t1
CROSS JOIN data AS t2
ON t1.userid=t2.userid
WHERE t1.userid='154'
(This query presumes that there always will be one row with the userid in both tables. You should use LEFT JOIN instead of CROSS JOIN to return a row even if there is no row in data for the userid. 154 is just an example userid.)
Have a look at http://dev.mysql.com/doc/refman/5.5/en/join.html for information on the JOIN syntax.
users
| user_id | username | password | enabled |
|---------|----------|----------|---------|
| 1 | john | sgsd2gg | 1 |
| 2 | jane | sdshdhd | 0 |
users_data
|udata_id| user_id | some_column |
|--------|---------|-------------------|
| 1 | 1 | Some title |
| 2 | 2 | another title |
Since you haven't posted your table schema, I can't give you an exact solution. But supposing you have a users table and a users_data table, where users_data are owned by a user. You can do a join on the table to retrieve all the data.
SELECT * -- Don't select all fields unless you need it
FROM users U LEFT JOIN users_data UD ON U.user_id = UD.user_id
WHERE U.user_id = 1
This would pull all the records for user with an ID of 1. This is a very simplistic join, but it should give you an idea.
Here's an example that visually describes the different options you can use : SQL Join Differences