mysql multiple many to many joins - mysql

I have events, events_styles, events_formats tables (an event can have many styles and many formats)
I am trying to filter events which have a events_styles join and events_formats join. So the query should select all events which are a particular style AND a particular format - my attempts so far:
SELECT * FROM events
JOIN events_styles ON events.id = events_styles.event_id
JOIN events_formats ON events.id = events_formats.format_id
WHERE events_styles.style_id = 3 AND events_formats.format_id = 1;
Empty set (0.00 sec)
SELECT * FROM events_styles
WHERE events_styles.style_id = 3
+----------+----------+
| event_id | style_id |
+----------+----------+
| 3 | 3 |
| 2 | 3 |
| 4 | 3 |
+----------+----------+
3 rows in set (0.00 sec)
SELECT * FROM events_formats
WHERE events_formats.format_id = 1
+----------+-----------+
| event_id | format_id |
+----------+-----------+
| 1 | 1 |
| 3 | 1 |
| 4 | 1 |
+----------+-----------+
3 rows in set (0.00 sec)
So the first query should return the event with id 4?
Im sure I need to combine 2nd and 3rd query into a subquery but Im unsure of the syntax - thanks

You are very close!
Your query should be:
SELECT * FROM events
JOIN events_styles ON events.id = events_styles.event_id
JOIN events_formats ON events.id = events_formats.event_id
WHERE events_styles.style_id = 3
AND events_formats.format_id = 1;
By joining on format_id and not event_id you are only going to get event_id = 1 which has no correspoding style id of 3. That's where you went wrong :-)

Related

how to use sum function with join in MySQL

select sum(bales) as bales
from receive_bardana
join receive_wheat
on receive_bardana.id = receive_wheat.id
where id=1
my result is showing wrong output
Your query 1)should not even syntax given id is ambiguous in the where clause 2) the joined table is pointless since you don't use anything from it in the select 3) From the little information you have provided in the question there is a 1 to many relationship between receive_bardana and receive_wheat which means that the sum is over all the joined rows for example
create table receive_bardana(id int,bales int);
create table receive_wheat(id int);
insert into receive_bardana values (1,10),(2,20);
insert into receive_wheat values(1),(1),(2),(2),(2);
select *
from receive_bardana
join receive_wheat
on receive_bardana.id = receive_wheat.id
where receive_bardana.id;
Result
+------+-------+------+
| id | bales | id |
+------+-------+------+
| 1 | 10 | 1 |
| 1 | 10 | 1 |
| 2 | 20 | 2 |
| 2 | 20 | 2 |
| 2 | 20 | 2 |
+------+-------+------+
5 rows in set (0.00 sec)
and with your (fixed) query
select sum(bales) as bales
from receive_bardana
join receive_wheat
on receive_bardana.id = receive_wheat.id
where receive_bardana.id;
the sum correctly returns
+-------+
| bales |
+-------+
| 80 |
+-------+
1 row in set (0.00 sec)
Which explains fully the 'problem' you are having.
If you want a answer to what you are trying to do I suggest you raise a new question describing what you are trying to do rather than just saying this lump of code does not do as I expect (in fact it does what I expect and is not 'wrong')

Query to get subjects of interest for all User Y where Y shares >=3 interests with a User X

These are two tables from a part of supposed Twitter like database where users can follow other users. The User.name field is unique.
mysql> select uID, name from User;
+-----+-------------------+
| uID | name |
+-----+-------------------+
| 1 | Alice |
| 2 | Bob |
| 5 | Iron Maiden |
| 4 | Judas Priest |
| 6 | Lesser Known Band |
| 3 | Metallica |
+-----+-------------------+
6 rows in set (0.00 sec)
mysql> select * from Follower;
+-----------+------------+
| subjectID | observerID |
+-----------+------------+
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 2 |
+-----------+------------+
7 rows in set (0.00 sec)
mysql> call newFollowSuggestionsForName('Bob');
+-------------------+
| name |
+-------------------+
| Lesser Known Band |
+-------------------+
1 row in set (0.00 sec)
I want to make an operation that will suggest for a user X a list of users they may be interested in following. I thought one heuristic could be to show X for all y who user y follows where X and y follow at least 3 of the same Users. Below is the SQL I came up with to do this. My question is if it could be done more efficiently or nicer in some other ways.
DELIMITER //
CREATE PROCEDURE newFollowSuggestionsForName(IN in_name CHAR(60))
BEGIN
DECLARE xuid INT;
SET xuid = (select uID from User where name=in_name);
select name
from User, (select subjectID
from follower
where observerID in (
select observerID
from Follower
where observerID<>xuid and subjectID in (select subjectID from Follower where observerID=xuid)
group by observerID
having count(*)>=3
)
) as T
where uID = T.subjectID and not exists (select * from Follower where subjectID=T.subjectID and observerID=xuid);
END //
DELIMITER ;
Consider the following refactored SQL code (untested without data) for use in stored procedure.
select u.`name`
from `User` u
inner join
(select subf.observerID, subf.subjectID
from follower subf
where subf.observerID <> xuid
) f
on u.UID = f.subjectID
inner join
(select f1.observerID
from follower f1
inner join follower f2
on f1.subjectID = f2.subjectID
and f1.observerID <> xuid
and f2.observerID = xuid
group by f1.observerID
having count(*) >= 3
) o
on f.observerID = o.observerID
I think the basic query starts as getting all "observers" who share three "subjects" with a given observer:
select f.observerid
from followers f join
followers f2
on f.subjectid = f2.subjectid and
f2.observerid = 2
group by f.observerid
having count(*) = 3;
The rest of the query is just joining in the names to fit into your paradigm of using names for references rather than ids.

Querying MySQL tables for a item a user hasnt 'scored' yet

Tables
__________________ ________________________________
|______name________| |____________scores______________|
|___id___|__name___| |_id_|_user-id_|_name-id_|_score_|
| 1 | bob | | 1 | 3 | 1 | 5 |
| 2 | susan | | 2 | 1 | 3 | 4 |
| 3 | geoff | | 3 | 3 | 2 | 3 |
| 4 | larry | | 4 | 2 | 4 | 5 |
| 5 | peter | | 5 | 1 | 1 | 0 |
-------------------- ----------------------------------
Im looking to write a query that returns a RANDOM name from the 'name' table, that the user hasnt scored so far.
So given user '1' for example, it could return 'susan, larry or peter' as user '1' hasnt given them a score yet.
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id
WHERE votes.user_id = 1
AND (votes.score IS NULL);
So far I have this, but it doesnt seem to be working as I would like
(atm it doesnt return a random, but all, but this is wrong)
Any help would be appreciated.
If you are filtering on some field of outer joined table type of join is automatically changed to inner. In your case it's condition
votes.user_id = 1
So you need to move that condition from WHERE to ON
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id and votes.user_id = 1
WHERE (votes.score IS NULL);
Consider moving the condition from WHERE to JOIN ON clause since you are performing an OUTER JOIN else the effect would be same as INNER JOIN
LEFT JOIN votes
ON names.id = votes.name_id
AND votes.user_id = 1
WHERE votes.score IS NULL
ORDER BY RAND();
You could apply :
SELECT name FROM name join scores on name.id=scores.user_id WHERE scores.score=0
You can perform this as a sub-query
SELECT *
FROM names
WHERE id NOT IN (SELECT name_id FROM votes WHERE user_id=1)
ORDER BY RAND()
LIMIT 1

Get records from one table and a corresponding table

I have two tables:
orders
poid | user | pid | payment_id
1 | 1 | 1 | abc123
2 | 2 | 2 | def345
orders_addon
poaid | user | poid | pid
1 | 1 | 1 | 3
2 | 1 | 1 | 5
One represents orders, the second one represent addons a user can add to his order.
There is always a row in orders and it can occur that there is no matching orders_addon for an order.
I'm looking for a query that returns matching rows from orders and orders_addon if there are matching ones.
SELECT user,pid FROM ... WHERE payment_id = 'abc123'
Should return
user | pid
1 | 1
1 | 3
1 | 5
And the same query should only return results from the orders table if there is no matching record in the orders_addon table.
SELECT user,pid FROM ... WHERE payment_id = 'def345'
user | pid
2 | 2
I reckon this could be done using UNION but then I wouldn't be able to match the tables and it would become a problem since the orders_addon table doesn't have a payment_id
Use LEFT JOIN WITH IF STATMENT
mysql> ( SELECT u.user,IFNULL(ua.pid ,u.pid) as pid
FROM orders u
inner JOIN orders_addon ua on ua.poid=u.poid
WHERE u.payment_id = 'abc123'
)
union all
( SELECT u.user,u.pid
from orders u
where u.payment_id = 'def345'
);
+------+------+
| user | pid |
+------+------+
| 1 | 3 |
| 1 | 5 |
| 2 | 2 |
+------+------+
3 rows in set (0.00 sec)
mysql> ( SELECT u.user,IFNULL(ua.pid ,u.pid) as pid
FROM orders u
inner JOIN orders_addon ua on ua.poid=u.poid
WHERE u.payment_id = 'def345'
)
union all
( SELECT u.user,u.pid
from orders u
where u.payment_id = 'def345'
);
+------+------+
| user | pid |
+------+------+
| 2 | 2 |
+------+------+
1 row in set (0.00 sec)

MySQL inner join among three tables syntax

My tables:
mysql> select * from pvf_order;
+------+------------+------+
| oid | orderdate | cid |
+------+------------+------+
| 1001 | 2014-02-10 | 1 |
10 rows in set (0.00 sec)
mysql> select * from pvf_order_item;
+------+-----+----------+
| oid | pid | quantity |
+------+-----+----------+
| 1001 | 1 | 2 |
18 rows in set (0.00 sec)
mysql> select * from pvf_product;
+-----+----------------------+----------------+--------+--------------+
| pid | prod_desc | Prod_finish | price | prod_line_id |
+-----+----------------------+----------------+--------+--------------+
| 1 | End Table | Cherry | 175.00 | 1 |
8 rows in set (0.01 sec)
Right now my query has been variations of:
SELECT
pvf_order.cid,
pvf_order.orderdate,
pvf_product.prod_desc,
pvf_order_item.quantity,
pvf_product.price
FROM pvf_order
INNER JOIN pvf_order.oid=pvf_order_item.oid
INNER JOIN pvf_order_item.pid=pvf_product.pid
WHERE YEAR(pvf_order.orderdate)=2014 AND MONTH(pvf_order.orderdate)=10;
I just don't know exactly where I'm going wrong. I edited some of the info out just to avoid a big wall of text, but I hope the general idea is there. Any help is appreciated!
Check JOIN sintaxis, also use ALIAS for table names
SELECT
o.cid,
o.orderdate,
p.prod_desc,
oi.quantity,
p.price
FROM pvf_order o
INNER JOIN pvf_order_item oi
ON o.oid = oi.oid
INNER JOIN pvf_product p
ON pvf_o.pid = p.pid
WHERE YEAR(o.orderdate) = 2014 AND MONTH(o.orderdate) = 10;
Also instead of use Year and Month function, you can simplify to one single comparasion operation.
WHERE date_format(o.orderdate, '%Y-%m') = '2014-10'