Join to give list with optional relationship column - mysql

Tables:
users: id INT
items: id INT
setid INT
sets: id INT
relationships: userid INT
itemid INT
relationship ENUM('owner', 'participant')
Given a userid and a setid, we need to generate a list of all the items in the set, and the user’s relationship to each item, if a relationship exists. i.e. the results would
setid itemid relationship
---------------------------------
1 1 NULL
1 2 owner
....
The following doesn't work, because the second where clause eliminates rows where relationship is null:
select
sets.id as setid,
items.id as itemid,
relatonships.relationship as relationship
from sets
inner join items on sets.id = items.setid
left join relationships on relationships.itemid = items.id
where
sets.id = 5
and relationships.userid = 27
However, the second where clause eliminates rows where there is no existing relationship between that item and the given user. How can this be done with a single query?

It was very simple, I didn't understand you could test against a constant in a 'join ... on' clause:
select
sets.setid,
items.itemid,
relationships.relationship
from sets
inner join items on items.setid = sets.id
left join relationships
on relationships.itemid = items.id
and relationships.userid = 5
where
sets.id = 1

Not 100% what you are asking, by try this
select
sets.id as setid,
items.id as itemid,
relatonships.relationship as relationship
from sets
inner join items on sets.id = items.event
LEFT join relationships on relationships.itemid = items.id
where
sets.id = [say, 5]
and relationships.userid = [say, 27]
Keep in mind that the relationship column will be NULL if none exists. Also, the relationship reference in the WHERE clause could eliminate rows as well. Hope this gets you start, again I might misunderstand what you are trying to achieve. From your table design, there does not seem to be a relationship between the user and the sets, could you possibly the list of fields in the tables.
Try the following query:
select sets.id as setId,
items.id as ItemId,
relationships.relationship
from sets
join users on 1=1
join items on sets.id=items.setid
left join relationships on relationships.itemId = items.id
where
sets.id=5 and users.id = 27
By adding the users table to the join with 1=1, you do not need the where clause to reference the relationship table at all
Change the left join to:
left join relationships on relationships.itemId = items.id
AND relationships.userId=users.id

Related

Get all values from intermediate table

I have a mySQL three-table many-to-many setup with link table, simplified here:
categories
category_id
category_name
categories2entries
category_id,
entry_id
entries
entry_id
entry_text
I need to return all entries for a given category name but I want to include a list of all the categories a given entry is attached to. For example, if a search is for Addictions and the entry is also listed in categories Mental health and Young people, I want an output field for each result with all three categories listed.
How do I do this?
You need direct lookup for to get the list of entries, and backward lookup for to get categories list for each entry. Backward lookup must use another tables copies.
SELECT e.entry_text,
GROUP_CONCAT(c2.category_name) belongs_to
FROM categories c1
JOIN categories2entries ce1 ON c1.category_id = ce1.category_id
JOIN entries e ON ce1.entry_id = e.entry_id
JOIN categories2entries ce2 ON ce2.entry_id = e.entry_id
JOIN categories c2 ON c2.category_id = ce2.category_id
WHERE c1.category_name = 'Category Name'
GROUP BY e.entry_text
If you need the same for more than one category (maybe even all) then
SELECT c1.category_name,
e.entry_text,
GROUP_CONCAT(c2.category_name) belongs_to
FROM categories c1
JOIN categories2entries ce1 ON c1.category_id = ce1.category_id
JOIN entries e ON ce1.entry_id = e.entry_id
JOIN categories2entries ce2 ON ce2.entry_id = e.entry_id
JOIN categories c2 ON c2.category_id = ce2.category_id
/* WHERE c1.category_name IN ({Category Names list}) */
GROUP BY c1.category_name,
e.entry_text

SQL GROUP_CONCAT with LEFT JOIN to multiple relative rows

I have been stuck on this for hours now, any help would be greatly appreciated
I have two tables 'products' and 'product_subcategorys'
'products' holds only unique ids where as 'product_subcategorys' holds multiple ids relative to the 'products' table
'products'
id brand
1 a
2 b
3 a
'product_subcategorys'
id subcat
1 u
1 i
2 u
3 u
this is the query I have, Group by 'p.id' doesn't appear to work
SELECT GROUP_CONCAT(p.brand)
FROM products p
LEFT JOIN product_subcategorys s ON p.id = s.id
WHERE (
s.subcategory = "u"
OR s.subcategory = "i"
) AS GROUPbrand
So my problem is, i want it to return the list of brands only from the 'product' table I cant use distinct because i need to count the multiples
I want the query to return brand 'a' twice, but this query is returning it 3 times since there are two matching ids in the 'product_subcategorys'
Does this do what you want?
SELECT GROUP_CONCAT(p.brand ORDER BY p.id)
FROM products p
WHERE EXISTS (SELECT 1
FROM product_subcategorys s
WHERE p.id = s.id AND
s.subcategory IN ('u', 'i')
);

Pull data from 3 tables

I have 3 tables as follows :
Table 1: Product
id_product [Primary Key],added_time.
Table 2: Category
id_category [Primary Key],Category_name.
Table 3: product_category
id_category,id_product [Both Foreign Keys]
I want to pull Data as
Category_name,No Of Products in this Category,Last time when product was added to Category(Latest product added_time).
You could use this SQL:
SELECT Category.Category_name,
Count(DISTINCT Product.id_product) AS num_products,
Max(Product.added_time) last_added_time
FROM Category
LEFT JOIN product_category
ON product_category.id_category = Category.id_category
LEFT JOIN Product
ON Product.id_product = product_category.id_product
GROUP BY Category.Category_name;
Note that by using LEFT JOIN you will be certain to list all categories even those for which no products exist. If you don't want those, replace both LEFT keywords with INNER.
Note also that in standard SQL you need to GROUP BY any columns you mention in the SELECT list, unless they are aggregated, like with MAX or COUNT.
SELECT C.`Category_name`,
(SUM(IF(P.`id_product`IS NULL,0,1))) AS No_of_Products,
MAX(P.`added_time`) AS Latest_time
FROM
Category C
LEFT JOIN
product_category P_C ON C.`id_category` = P_C.`id_category`
LEFT JOIN
Product P ON P.`id_product` = P_C.`id_product`
GROUP BY C.`id_category`
Hope this helps.

MySQL Joins on n to n relational table keping NULL values

This is a vote system, where candidate can be voted from different(limited) places. and I want the number of vote per place of each candidate.
I have 3 tables
TABLE candidate
------------------
id
name
TABLE place
------------------
id
label
TABLE vote
------------------
id
id_candidate
id_vote
no_votes // represents the amount of votes in this place for that particular candidate
Suppose I have 10 candidates and 15 different places, I'm trying to make a query that will return 10*15 = 150 rows even if there is no votes, keeping NULL value for ids that are not present in the relational table(which i can replace by 0).
But i'm not making the correct query
Here is the query i made so far (i've tried many modification, inner, outer joins... but nothing worked)
SELECT *
FROM votes
RIGHT JOIN candidate ON candidate.id = candidate_id
LEFT JOIN palce ON place.id = place_id
First, if you want the number of votes per candidate, then you should be thinking "aggregation".
Second, don't mix left and right joins in a query. It is just confusing. Start with the table where you want to keep all the rows, and then just use left join.
So, something like this:
SELECT c.*,
SUM(p.place_name = 'place1') as place1,
SUM(p.place_name = 'place2') as place2,
SUM(p.place_name = 'place3') as place3
FROM candidate c LEFT JOIN
votes v
ON c.id = v.candidate_id LEFT JOIN
place p
ON p.id = v.place_id
GROUP BY c.id;
Considering:
TABLE vote
------------------
id PK
id_candidate FK to candidate
id_vote FK to place
no_votes
-
SELECT CA.name,
PL.label,
SUM(VO.no_votes) as votes
FROM candidate CA
LEFT JOIN vote VO ON CA.id = VO.id_candidate
LEFT JOIN place PL ON PL.id = VO.id_vote
GROUP BY CA.id, PL.id

SQL SUM Help - Only returning 1 field

My SQL is only returning one field when it should be returning one for each user.
Any idea where I'm going wrong? If you need additional information I can provide, but I'm just not sure where to go with this at the moment.
Here is my SQL:
SELECT uId, uForename, SUM(biProductPrice * biQuantity) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
GROUP BY uId, uForename
any columns starting with u belong to the users table.
any columns starting with ord belong to the orders table.
any columns starting with b belong to the basket table.
any columns starting with bi belong to the basketitems table.
EDIT:
Everything now works fine except for my SUM, there are only 2 fields with an ordStatus between 4 and 50, so they are the only ones that apply, the biQuantity for one is 8 and the biProductPrice is 100, the other field has a biQuantity of 1 and a biProductPrice of 100, why is it returning a value of 400?
Group by the user and the sum will be returned for each one
SELECT users.id, users.name, SUM(biProductPrice) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
group by users.uId, users.name
SELECT users.id, users.name, SUM(biProductPrice) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
group by users.uId, users.name