I am attempting to JOIN onto two different columns in the first table below from columns in the second and third tables.
I wish to JOIN users.id to job_listings.id to return users.username, and to also JOIN and delimit job_listings.categories to job_categories.id to return job_categories.description via FIND_IN_SET
job_listings
id | employer_id | categories
1 | 1 | 1,2
2 | 1 | 2
users
id | username | type
1 | foo | employer
2 | wat | employer
job_categories
id | description
1 | fun
2 | hak
I desire output that is of the following format:
output
username | type | category | description
foo | employer | 1 | fun
foo | employer | 2 | hak
foo | employer | 2 | hak
I have tried using various permutations of the following code:
SELECT users.username, users.type, job_listings.categories FROM users
JOIN job_listings ON users.id
JOIN job_listings AS category ON FIND_IN_SET(category.categories, job_categories.id)
ORDER BY users.username, category.categories
I know from other answers that I need to use an alias in order to use multiple JOIN operations with the same table, but despite adapting other answers I keep receiving errors related to declaring an alias, or returning output that has a column with the alias but no data returned in that column.
First, you should normalize your design. You should not store integer values in strings. You should not have foreign key references that you cannot declare as such. You should not store lists in strings. Is that enough reasons? You want a junction table for JobCategories with one row per job and one row per category.
Sometimes, we are stuck with other peoples lousy decisions and cannot readily change them. In that case, you want a query like:
SELECT u.username, u.type, jc.id, jc.category
FROM users u JOIN
job_listings jl
ON u.id = jl.employer_id and u.type = 'employer' join
job_categories jc
ON FIND_IN_SET(jc.id, j.categories) > 0
ORDER BY u.username, jc.category;
This query cannot take advantage of indexes for the category joins. That means that it will be slow. The proper data structure -- a junction table -- would fix this performance problem.
Related
I have two tables in my database where the first one has related values of the second one. Just like this:
table "people"
ID | NAME | SCHOOL
-------------------------
1 | john | 2
2 | fred | 1
3 | maria | 3
table "school"
ID | NAME
-------------------------
1 | first school
2 | second school
3 | third school
Ok.
I'm trying to make a select in "people" table and get the "SCHOOL" number replaced by "school" table related id.
So I did this:
"SELECT * FROM people A LEFT JOIN school B ON A.school = B.id"
That's ok!
But If I have to get the "people" ID value in this return, it will be replaced by "school" table ID value.
How can I solve this?
Thanks a lot!
If you are learning SQL, learn to list all the columns you want. Explicitly:
SELECT p.id, p.name, s.name as school_name
FROM
people p LEFT JOIN
school s
ON p.school = s.id;
Notes:
The table aliases are abbreviations for the table names. This makes the query much easier to follow.
A LEFT JOIN is not really needed here, but you are using it.
s.name could be aliased (s.name as school_name) to distinguish it from the person's name.
Hey I try to select a row from a table with two matching entries on another one.
The structure is as following:
----------------- ---------------------
| messagegroups | | user_messagegroup |
| | | |
| - id | | - id |
| - status | | - user_id |
| | | - messagegroup_id |
----------------- | |
---------------------
There exist two rows in user_messagegroup with the ids of two users and both times the same messagegroup_id.
I would like to select the messagegroup where this two users are inside.
I dont get it.. so I would appreciate some help ;)
The specification you provide isn't very clear.
You say "with the ids of two users"... if we take that to mean you have two user_id values you want to supply in the query, then one way to to find the messagegroups that contain these two specific users:
SELECT g.id
, g.status
FROM messagegroups g
JOIN ( SELECT u.messagegroup_id
FROM user_messagegroup u
WHERE u.user_id IN (42, 11)
GROUP BY u.messagegroup_id
HAVING COUNT(DISTINCT u.user_id) = 2
) c
ON c.messagegroup_id = g.id
The returned messagegroups could also contain other users, besides the two that were specified.
If you want to return messagegroups that contain ONLY these two users, and no other users...
SELECT g.id
, g.status
FROM messagegroups g
JOIN ( SELECT u.messagegroup_id
FROM user_messagegroup u
WHERE u.user_id IS NOT NULL
GROUP BY u.messagegroup_id
HAVING COUNT(DISTINCT IF(u.user_id IN (42,11),u.user_id,NULL)) = 2
AND COUNT(DISTINCT u.user_id) = 2
) c
ON c.messagegroup_id = g.id
For improved performance, you'll want suitable indexes on the tables, and it may be possible to rewrite these to eliminate the inline view.
Also, if you only need the messagegroup_id value, you could get that from just the inline view query, without the need for the outer query and the join operation to the messagegroups table.
I have two simple tables, one called itineraries that holds details of holiday itineraries and one called users, that holds details of users. Other users create itineraries, and users can copy their itineraries and add travel agents, so the copied_from_id is the ID of the original creating user from users.id.
I've joined itineraries.user_id to users.id using the below query which works perfectly:
SELECT
itineraries.travel_agent_id,
itineraries.copied_from_id,
itineraries.user_id,
users.full_name,
users.username
FROM `gadabouting_gadabouting_production`.`itineraries`
INNER JOIN `gadabouting_gadabouting_production`.`users` ON itineraries.user_id=users.id
WHERE itineraries.travel_agent_id='253'
Giving me the following output:
+-----------------+------------------+---------+-------------+-------------+
| travel_agent_id | original_creator | user_id | full_name | username |
| 253 | 501 | 1465 | John Smithy | j.smithy |
| 253 | 501 | 1465 | John Smithy | j.smithy |
| 253 | 501 | 1474 | Ben Stockes | ben.stockes |
+-----------------+------------------+---------+-------------+-------------+
(The travel_agent_id and original_creator columns are the same as users.id).
What I want to do now is map the itineraries.travel_agent_id and itineraries.original creator to the users.full_name and users.username columns (so have the full_name and username columns printed next to each of the travel_agent_id and original_creator columns, but I just can't work out how to do it. I've spent hours on it now and can't get my head round it. Do I need to do more joins?
I've looked at several other SO questions about multiple joins but as far as I can see, none of them cover the process of 'going back' again and again on the same column as I want to do here.
Is this possible? Would greatly appreciate any help!
Thanks
You can join in the same table over and over, but you need to use an alias for each one so that you can specify which one you want to use. If you access the result by name, you also need alases for some of the field names.
(It's conventient to use aliases on other tables also, to make the query less verbose.)
select
i.travel_agent_id,
i.copied_from_id,
i.user_id,
u.full_name,
u.username,
ut.full_name as travel_agent_full_name,
ut.username as travel_agent_username,
uc.full_name as creator_full_name,
uc.username as creator_username
from
gadabouting_gadabouting_production.itineraries as i
inner join gadabouting_gadabouting_production.users as u on u.id = i.user_id
inner join gadabouting_gadabouting_production.users as ut on u.id = i.travel_agent_id
inner join gadabouting_gadabouting_production.users as uc on u.id = i.original_creator
where
i.travel_agent_id = '253'
I have a query that selects all columns from multiple tables, but it's returning multiples of the same values (I only want distinct values).
How can I incorporate something like this? When I try this, it still
Select Distinct A.*, B.*, C.*....
Does distinct only work when selecting the column names and not all (*) ? In this reference it says distinct in reference to column names, not across all of the tables. Is there any way that I can do this?
edit - I added more info below
Sorry guys, I just got back onto my computer. Also, I just realized that my query itself is the issue, and Distinct has nothing to do with it.
So, the overall goal of my Query is to do the following
Generate a list of friends that a user has
Go through the friends and check their activities (posting, adding friends, etc..)
Display a list of friends and their activities sorted by date (I guess like a facebook wall kind of deal).
Here are my tables
update_id | update | userid | timestamp //updates table
post_id | post | userid | timestamp //posts table
user_1 | user_2 | status | timestamp //friends table
Here is my query
SELECT U.* , P.* ,F.* FROM posts AS P
JOIN updates AS U ON P.userid = U.userid
JOIN friends AS F ON P.userid = F.user_2 or F.user_1
WHERE P.userid IN (
select user_1 from friends where user_2 = '1'
union
select user_2 from friends where user_1 = '1'
union
select userid from org_members where org_id = '1'
union
select org_id from org_members where userid = '1'
)
ORDER BY P.timestamp, U.timestamp, F.timestamp limit 30
The issue I'm having with this (that I thought was related to distinct), is that if values are found to meet the requirements in, say table Friends, a value for the Posts table will appear too. This means when I'm displaying the output of the SQL statement, it appears as if the Posts value is shown multiple times, when the actual values I'm looking for are also displayed
The output will appear something like this (notice difference between post value in the rows)
update_id | update | userid | timestamp | post_id | post | userid | timestamp | user_1 | user_2 | status | timestamp
1 | update1 | 1 | 02/01/2013 | 1 | post1| 1 | 2/02/2013| 1 | 2 | 1 | 01/30/2013
1 | update1 | 1 | 02/01/2013 | 2 | post2| 1 | 2/03/2013| 1 | 2 | 1 | 01/30/2013
So, as you can see, I thought I was having a distinct issue (because update1 appeared both times), but the query actually just selects all the values regardless. I get the results I'm looking for in the Post table, but all the other values are returned. So, when I display the table in PHP/HTML, the Post value will display, but I also get duplicates of the updates (just for this example)
When you select distinct *, you select every row, including the one that makes the record unique. If you want something better than what you are getting, you have to type the individual column names in your select clause.
It would be easy if you explain a little more what is the connection between the tables you'r querying, because you can use joins, unions (as mentioned above) or even group by's ...
Your updated post shows one of the JOIN conditions as:
JOIN friends AS F ON P.userid = F.user_2 OR F.user_1
This is equivalent to:
JOIN friends AS F ON (P.userid = F.user_2 OR F.user_1 != 0)
and will include many rows that you did not intend to include.
You probably intended:
JOIN friends AS F ON (P.userid = F.user_2 OR P.userid = F.user_1)
I think you want this:
select *
from tableA
union
select *
from tableB
union
select *
from tableC
This assumes that HHS tables all have the same number of columns and they are of the same data type. This not, you'll have to select specific columns to make it so.
Look at this DB schema:
The users can order products. For any order I add a new record in the orders table and a new record in orders-products N:M table for any single product ordered by the user (and fill pieces field). But, when a user creating an order, I need to show the list of all products and fill the pieces field with the quantity ordered for any product.
Usually, to do it, I use two queries.
The first query get all products in the products table:
SELECT * FROM products;
The second query gets the products ordered in the orders-products table filtering them by orders-idorder FK:
SELECT orders_idorder AS idorder, products_idproduct AS idproduct, pieces FROM orders_products WHERE orders_idorder=1;
Then I merge the results in a single array and use it to display the complete list of products with the pieces ordered for each one. The final result is something like this:
+---------+-----------+--------------+-------+--------+
| idorder | idproduct | description | price | pieces |
+---------+-----------+--------------+-------+--------+
| 1 | 1 | Product 1 | 10.20 | 2 |
| 1 | 2 | Product 22 | 11.00 | NULL |
| 1 | 3 | Product 333 | 19.22 | NULL |
| 1 | 4 | Product 4444 | 9.20 | NULL |
+---------+-----------+--------------+-------+--------+
Note: In the above example there are 4 records in the products table and just 1 record in the orders-products table (it has idorder=1, idproduct=1 and pieces=2).
-> Here you can find the SQL Dump to test the queries.
Merging arrays built from 2 queries is the best way?
I can do it with a single query?
What do you think about the performance?
Generally letting the databse optimize the merge will be better than what you can do in code. The db is usually better at sorting too.
How you structure the query depends on your desired result set. If you want all products regardless of whether they appear in the orders table then you'd use an OUTER JOIN otherwise an INNER JOIN would filter out products that have never been ordered.
If you give us your desired results for some sample data we might be able to help you with the query, but give it a shot yourself first.
set #searchOrderId = 1;
select ifnull(op.orders_idorder, #searchOrderId) AS idOrder,
p.idproduct,
p.description,
p.price,
op.pieces
from products p
left join orders_products op on op.products_idproduct = p.idproduct
where ifnull(op.orders_idorder, #searchOrderId) = #searchOrderId ;
SQL Fiddle I was playing with to test it.
Sure you can get the information using a single query by JOINing the tables together. Is it what you're having troubles with?
I think that in general, executing a single query to retrieve the results will perform better than executing two queries and merging the results. It is impossible to say what the ideal plan for execution is, given the information provided. Table sizes, architecture, etc will play a role.
For retrieving all of the products from a single order, try:
select
o.idorder,
p.idproduct,
p.description,
p.price,
op.pieces
from
orders o
inner join orders_products op
on o.idorder = op.orders_idorder
inner join products p
on op.products_idproduct = p.idproduct
where
o.idorder = 1
I see, you wanted a LEFT JOIN from Products table into Orders_Products. This will give you result you're looking for.