I have table called users and table called events.
Every user sets is own preferred "area codes".
Every event is set to some area codes, and this information is saved in a table:
events_areas:
area_id BIGINT
event_id BIGINT
I am trying to find a good way to let the user select is own area codes... and then to match it in a select statement with the event area codes.
i tought about doing the same as events_areas and do: users_areas:
area_id BIGINT
user_id BIGINT
But then I dont know how to match them in select statement... ?
Thanks
Assuming the tables you listed, the following would select all events in the users selected area codes.
SELECT u.*, e.*
FROM users u
JOIN users_areas ua
ON u.id = ua.user_id
JOIN events_areas ea
ON ea.area_id = ua.area_id
JOIN events e
ON ea.event_id = e.id
The select statement would look something like this:
SELECT DISTINCT users.*, events.*
FROM users
JOIN users_areas ON users.user_id = users_areas.user_id
JOIN events_areas ON users_areas.area_id = event_areas.area_id
JOIN events ON events_areas.event_id = events.event_id
It's useful to start listing the relations between the tables (objects) in plain English. For example:
A user belongs to an area code and an area code has many users.
An event happens in an area code and an area code has many events.
Both statements describe many-to-many relations, so you do need those two tables (sometimes called cross-reference tables).
Writing those statements always helps to understand the problem and ask questions about it. Assuming the statements above are correct, then having those two tables users_areas and events_areas is correct too. To build the select statement, note that there's only one column that both tables have in common: area_id (which by the way, would fit in a int, using bigint is a huge overkill, and I think the same goes for user_id and event_id). So area_id is the column you need to use to match them (the correct term is to join them).
Here's your select statement:
SELECT ea.event_id, ea.area_id, ua.user_id
FROM events_areas ea
INNER JOIN user_areas ua ON ea.area_id = ua.area_id
Or, assuming you also have an event table and a user table, which is likely the case, we expand the select to look like this:
SELECT e.name, u.name
FROM events_areas ea
INNER JOIN user_areas ua ON ea.area_id = ua.area_id
INNER JOIN users u ON u.user_id = ua.user_id
INNER JOIN events e ON e.event_id = ue.event_id
Okay I changed this based on your comment...
Create these tables:
Table: user
id
name
Table: user_area
id
user_id
area_id
Table: event
id
name
Table: event_area
id
event_id
area_id
Table: area
id
area_code
Then run this query:
SELECT event.name FROM event, event_area WHERE event.id = event_area.event_id AND event_area.area_id IN (SELECT area_id FROM user_area WHERE user_id = <CURRENTUSERSID>)
SELECT *
FROM users u
INNER JOIN users_areas ua
ON u.user_id = ua.user_id
INNER JOIN events_areas ea
ON ua.area_id = ea.area_id
INNER JOIN events e
ON ea.event_id = e.event_id
Related
I have a products table where I include 3 columns, created_user_id, updated_user_id and in_charge_user_id, all of which are related to my user table, where I store the id and name of the users.
I want to build an efficient query to obtain the names of the corresponding user_id's.
The query that I build so far is the following:
SELECT products.*,
(SELECT name FROM user WHERE user_id = products.created_user_id) as created_user,
(SELECT name FROM user WHERE user_id = products.updated_user_id) as updated_user,
(SELECT name FROM user WHERE user_id = products.in_charge_user_id) as in_charge_user
FROM products
The problem with this query is that if I have 30,000 records, I am executing 3 more queries per row.
What would be a more efficient way of achieving this? I am using mysql.
For each type of user id (created, updated, in_charge) you would JOIN the users table once:
SELECT
products.*,
u1.username AS created_username,
u2.username AS updated_username,,
u3.username AS in_charge_username,
FROM products
JOIN user u1 ON products.created_user_id = u1.user_id
JOIN user u2 ON products.updated_user_id = u2.user_id
LEFT JOIN user u3 ON products.in_charge_user_id = u3.user_id
This is the best practice method to obtain the data.
It is similiar to your query with sub-selects but a more modern approach which I think the database can optimize and utilize better.
Important:
You need foreign key index on all the user_id fields in both tables!
Then the query will be very fast no matter how many rows are in the table. This requires an engine which supports foreign keys, like InnoDB.
LEFT JOIN or INNER JOIN ?
As the other answers suggest a LEFT JOIN, I would not do a left join.
If you have an user id in the products table, there MUST be a linked user_id in the user table, except for the in_charge_user which is only present some times. If not, the data would be semantically corrupt. The foreign keys assure that you always have a linked user_id and a user_id can only be deleted when there is no linked product left.
JOIN is equivalent to INNER JOIN.
You can use LEFT JOIN instead of subselects.
Your query should be like:
SELECT
P.*,
[CU].[name],
[UU].[name],
[CU].[name]
FROM products AS [P]
LEFT JOIN user AS [CU] ON [CU].[user_id] = [P].[created_user_id]
LEFT JOIN user AS [UU] ON [UU].[user_id] = [P].[updated_user_id]
LEFT JOIN user AS [CU] ON [CU].[user_id] = [P].[in_charge_user_id]
First, your query should be fine. You only need an index on user(user_id) or better yet user(user_id, name) for performance. I imagine that the first exists.
Second, you can write this using LEFT JOIN:
SELECT p.*, uc.name as created_user,
uu.name as updated_user, uin.name as in_charge_user
FROM products p LEFT JOIN
user uc
ON uc.user_id = p.created_user_id LEFT JOIN
user uu
ON uu.user_id = p.updated_user_id LEFT JOIN
user uin
ON uin.user_id = p.in_charge_user_id;
With one of the above indexes, the two methods should have very similar performance.
Also note the use of LEFT JOIN. This handles the case where one or more of the user ids is missing.
Try this below query
SELECT products.*, c.name as created_user,u.name as updated_user,i.name as in_charge_user
FROM products left join user c on(products.created_user_id=c.user_id ) left join user u on(products.updated_user_id=u.user_id ) left join user u on(products.in_charge_user_id=i.user_id )
Also as Gordon Linoff mentioned create index on user table will fetch your data faster.
I have two tables EVENTS, and USER
EVENTS has fields "id", "name", "date"
USER has 1 field "id"
I want an SQL query to get all of the EVENT names of only the events that have the matching id's in USER. In other words, take all of the id's from USER, find the matching id's in EVENTS, display the name of the events that have those id's.
specifically, i want to display a list in html with all of these names
Thank you!
You would need to use an inner join for this, to do this your SQL should look something like this:
SELECT events.id, name, user.id
FROM events
INNER JOIN user ON events.id = user.id;
I would also advise against using block capitals for table names etc. Not that there is any technical reason for it, only it makes queries etc more readable.
Hope this helps :)
I want an SQL query to get all of the EVENT names of only the events
that have the matching id's in USER.
create table EVENTS (id number,name varchar2(20), date1 date);
create table user1(id number);
select u.id,e.name
from
USER1 u
INNER JOIN
EVENTS e
ON u.id = e.id;
In other words, take all of the id's from USER, find the matching id's
in EVENTS, display the name of the events that have those id's
select u.id,e.name
from
USER1 u
LEFT OUTER JOIN
EVENTS e
ON u.id = e.id;
Use exists:
select e.*
from events e
where exists (select 1 from user u where u.id = e.id);
U can use it directly using a WHERE Statement
select u.id,e.name
from
USER u,EVENTS e
where u.id = e.id;
I have following structure
USER
user_id
name
EVENT
event_id
user_id
event_name
How can I write inner join in redbean?
SELECT * FROM event e join user u on u.user_id = e.user_id
also how can I change Primary key of table schema in redbeans
Try
SELECT u.user_ID, u.name, e.event_id, e.user_id, e.event_name
from user u
inner join event e
on u.user_id=e.user_id
as you can see. You select all the columns from table event. Of course by doing this, it will not include the columns from table user. Thus we will now include all the column you want to display one by one and of course with it's respective alias.
Okay, so I am trying to perform a query that has 4 tables,
users, events, event_roles, user_event_role.
The users can fill multiple roles. What i am trying to do is get a result that looks more like this:
User, event, Role(s)
So if user 'Bill' is associated with event 'Meeting' and 'Bill' Fills multiple roles instead of getting a result like this:
user event role
--------------------------
bill Meeting admin
bill Meeting director
how would I get my result to be like this
user event role role
----------------------------------
bill Meeting admin director
Here is a query that I'm trying to build off of.
Select *
FROM `users` u
LEFT JOIN `event_role` er ON u.user_id = er.user_id
LEFT JOIN `events` e ON er.event_id = e.event_id
The result you seek is not possible.
However there is something close:
SELECT
user,
event,
group_concat(role SEPARATOR ',') as roles
FROM
`users` u
LEFT JOIN `event_role` er
ON u.user_id = er.user_id
LEFT JOIN `events` e
ON er.event_id = e.event_id
GROUP BY u.user_id
which would yield:
user event roles
----------------------
bill Meeting admin,director
In either case you would need to adjust your logic to parse it correctly.
You cannot get such result, because you don't know how many roles there might be (i.e. columns count), but you can use GROUP_CONCAT that way:
SELECT *,
GROUP_CONCAT(event_roles.role SEPARATOR ',') as roles
FROM users
LEFT JOIN event_role USING(user_id)
LEFT JOIN events USING(user_id)
GROUP BY user_id
Using this query you will get all roles concatonated with ,. But be aware of limitation of GROUP_CONCAT, the default value is set to 1024 characters which might not be enough (see my.cnf).
Use Group_concat (http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html) like this.
To show that quickly, I'm using a database from the excellent MySQL intro book by Ben Forta (http://www.forta.com/books/0672327120/), no affiliation.
SELECT v.vend_id, GROUP_CONCAT(p.prod_id)
FROM products p
JOIN vendors v ON v.vend_id= p.vend_id
JOIN orderitems oi ON oi.prod_id = p.prod_id
GROUP BY v.vend_id;
I usually go with the join approach but in this case I am a bit confused. I am not even sure that it is possible at all. I wonder if the following query can be converted to a left join query instead of the multiple select in used:
select
users.id, users.first_name, users.last_name, users.description, users.email
from users
where id in (
select assigned.id_user from assigned where id_project in (
select assigned.id_project from assigned where id_user = 1
)
)
or id in (
select projects.id_user from projects where projects.id in (
select assigned.id_project from assigned where id_user = 1
)
)
This query returns the correct result set. However, I guess the repetition of the query that selects assigned.id_project is a waste.
You could start with the project assignments of user 1 a1. Then find all assignments of other people to those projects a2, and the user in the project table p. The users you are looking for are then in either a2 or p. I added distinct to remove users who can be reached in both ways.
select distinct u.*
from assigned a1
left join
assigned a2
on a1.id_project = a2.id_project
left join
project p
on a1.id_project = p.id
join user u
on u.id = a2.id_user
or u.id = p.id_user
where a1.id_user = 1
Since both subqueries have a condition where assigned.id_user = 1, I start with that query. Let's call that assignment(s) the 'leading assignment'.
Then join the rest, using left joins for the 'optional' tables.
Use an inner join on user that matches either users of assignments linked to the leading assignment or users of projects linked to the leading project.
I use distinct, because I assumen you'd want each user once, event if they have an assignment and a project (or multiple projects).
select distinct
u.id, u.first_name, u.last_name, u.description, u.email
from
assigned a
left join assigned ap on ap.id_project = a.id_project
left join projects p on p.id = a.id_project
inner join users u on u.id = ap.id_user or u.id = p.id_user
where
a.id_user = 1
Here's an alternative way to get rid of the repetition:
SELECT
users.id,
users.first_name,
users.last_name,
users.description,
users.email
FROM users
WHERE id IN (
SELECT up.id_user
FROM (
SELECT id_user, id_project FROM assigned
UNION ALL
SELECT id_user, id FROM projects
) up
INNER JOIN assigned a
ON a.id_project = up.id_project
WHERE a.id_user = 1
)
;
That is, the assigned table's pairs of id_user, id_project are UNIONed with those of projects. The resulting set is then joined with the user_id = 1 projects to obtain the list of all users who share the projects with the ID 1 user. And now it only remains to retrieve the details for those users, which in this case is done in the same way as in your query, i.e. using an IN clause.
I'm sorry to say that I don't have MySQL to thoroughly test the performance of this query and so cannot be quite sure if it is in any way better or worse than your original query or than the one suggested both by #GolezTrol and by #Andomar. Generally I tend to agree with #GolezTrol's comment that a query with simple (semi- or whatever-) joins and repetitive parts might turn out more efficient than an equivalent sophisticated query that doesn't have repetitions. In the end, however, it is testing that must reveal the final answer for you.