INNER JOIN ? outcomes on two columns - mysql

I'm a little bit confused about which join to apply and where.
I have a mysql database that does betting in an IRC client.
It stores usernames , their guess and the eventual outcome of the game they bet on
the outcomes_table is like this
+--------------+
| id outcome |
+--------------+
| 1 win |
| 2 lose |
+--------------+
the user_table is like this
+----+----------+----------+-------------------+
| id | username | guess_id | bettingsession_id |
+----+----------+----------+-------------------+
| 1 | name1 | 1 | 1 |
| 2 | name2 | 1 | 2 |
| 3 | name3 | 2 | 2 |
4 name1 1 2
+----+----------+----------+-------------------+
the betting_session_table is like this:
+----+---------+
| id | result |
+----+---------+
| 1 | 1 |
| 2 | 2 |
+----+---------+
I want to get a list of the bets of a user with their guess joined to the outcome and the result joined to the
eg:
select each row a different bet username, guess (win/lose), result (win/lose)
Something like:
SELECT *
FROM user_table
INNER JOIN betting_session_table ON bettingsession_id = betting_session_table.id
INNER JOIN outcomes_table ON guess_id = outcomes_table.id
INNER JOIN outcomes_table ON result = outcomes_table.id
WHERE username = 'name1'
However this doesn't work, not sure but I don't think it lets me join the outcomes_table.id twice to two different columns but I want to this because the user may bet 'win' but result 'lose' etc.
EG: I want to return
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
| id | username | guess_id | id | outcome | betting_session_id | id | result | id | outcome |
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
| 1 | name1 | 1 | 1 | win | 1 | 1 | 1 | 1 | win |
| 4 | name1 | 1 | 1 | win | 2 | 2 | 2 | 2 | lose |
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
EDIT:
In the end I used two separate alias for each join which seems to work; here is the code from the actual table that works rather than the cut down example above.
SELECT *
FROM `xcoins_betting_log` A
LEFT JOIN `xcoins_betting_session` B ON A.betting_session_id = B.id
LEFT JOIN `xcoins_common_tables`.`xcoins_betting_outcomes` C ON A.guess_id = C.id
LEFT JOIN `xcoins_common_tables`.`xcoins_betting_outcomes` D ON B.outcome_id = D.id
WHERE `user_id` =9

I'm not sure if this is what you want, but I hope so.
SELECT
usr.*,
res.outcome,
IF(res.id = usr.guess_id, 'User win', 'User lose') AS result
FROM user_table AS usr
INNER JOIN betting_session_table AS bet ON
bet.id = usr.bettingsession_id
INNER JOIN outcomes_table AS res ON
res.id = bet.result
WHERE usr.username = 'name1'
Choose correct join
The most common joins is LEFT and INNER. Lets say the users have placed their bets, but the football game (or whatever) isn't completed yet, then you won't have the row in the outcomes_table right? The game isn't finished so the results will come later.
If you use INNER JOIN, the row in the outcomes_table won't match for unfinished games --- INNER JOIN requires matches.
If you want to see the bets also before the game has started, you can use LEFT JOIN. LEFT JOIN won't remove rows that hasn't got any outcome, the users will still be listed --- LEFT JOIN doesn't care.
INNER JOIN: Game must have result
LEFT JOIN: Game might have result

Related

how to perform an outer join in mysql

I have a table A that contains tree columns, id, users ids and vehicle id. And a table B that contains vehicleid, and vehicle name.
Table A
---------------------------
| Id | User_id |Vehicle_id|
---------------------------
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
| 6 | 4 | 5 |
---------------------------
Table B
-------------------
| Id |Vehicle_name|
-------------------
| 1 | Car |
| 2 | Bike |
| 3 | Plane |
| 4 | Boat |
| 5 | Rocket |
-------------------
Given a user id, I need to get all vehicle names, that doesn't match with table A. I've tried Outer joins, but I can't manage to do get the info that i need.
For example: Given user id 1, the query should return Car and Rocket.
thanks in advance
This is simple enough using not in or not exists:
select b.*
from b
where not exists (select 1
from a
where a.vehicle_id = b.id and a.user_id = #a_user_id
);
I also thought of using a cross join and was able to get the output in case you are more comfortable with join logic.
SELECT CJOIN.USER_ID, CJOIN.VEHICLE_ID, CJOIN.VEHICLE_NAME
FROM
(SELECT DISTINCT A.USER_ID, B.ID AS VEHICLE_ID, B.VEHICLE_NAME FROM TABLE_A A CROSS JOIN TABLE_B B) CJOIN
LEFT JOIN
TABLE_A D
ON CJOIN.USER_ID = D.USER_ID AND CJOIN.VEHICLE_ID = D.VEHICLE_ID
WHERE D.USER_ID IS NULL AND D.VEHICLE_ID IS NULL;
First, I got all possible combinations of USER_ID x VEHICLE_ID by a cross join and used this table in a left join to pull records for which there is no match.

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

SQL Join vs Sub-query

I'm running MySQL 5.1.71. In my database there are three tables - load, brass and mfg with load being my "main" table. My goal is to query load and have mfg.name included in the results. I've tried various iterations of JOIN clauses vs sub-queries both with and without WHERE clauses. It seems this should be pretty trivial so I'm not sure how I can't arrive at the solution.
load
-------------------------
| id | desc | brass_id |
-------------------------
| 1 | One | 2 |
| 2 | Two | 1 |
-------------------------
brass
---------------
| id | mfg_id |
---------------
| 1 | 6 |
| 2 | 8 |
---------------
brass_mfg
------------------------
| id | name |
------------------------
| 6 | This Company |
| 8 | That Company |
------------------------
My desired results would be...
results
---------------------------
| load | mfg |
---------------------------
| One | That Company |
| Two | This Company |
---------------------------
A load ID will always have only a single brass ID
A brass ID will always have only a single mfg ID
EDIT
The previously provided sample data (above) has been updated. Also, below are the query I'm running and the results I'm getting. The company is wrong in each record that is returned. I've included in the query and the results the IDs across the tables. The company names that appear are not the names in for the IDs in the mfg table.
SELECT
load.id AS "load.id",
load.brass_id AS "load.brass_id",
brass.id AS "brass.id",
brass.mfg_id AS "brass.mfg_id",
brass_mfg.id AS "brass_mfg.id",
brass_mfg.name AS "brass_mfg.name"
FROM `load`
LEFT JOIN brass ON load.brass_id = brass.id
LEFT JOIN brass_mfg ON brass.id = brass_mfg.id
-----------------------------------------------------------------------------------------
| load.id | load.brass_id | brass.id | brass.mfg_id | brass_mfg.id | brass_mfg.name |
-----------------------------------------------------------------------------------------
| 1 | 2 | 2 | 6 | 2 | Wrong Company |
| 2 | 1 | 1 | 8 | 1 | Incorrect Company |
-----------------------------------------------------------------------------------------
Look at your tables and see what data relates to one another then build up joins table by table to get your desired output.
SELECT p.desc AS Product, m.name AS mfg
FROM product p
INNER JOIN lot l ON p.lot_id = l.id
INNER JOIN mfg m ON l.mfg_id = m.id
If this is single - single relationship, why having middle table?
In your case the best scenario is simple join.
SELECT pt.desc as Product, mfg.name as Mfs
FROM Product pt
Join Lot lt on lt.id = pt.lot_id
Join Mfg mf on mf.id = lt.mfg_id
You have an error in your join query.
Try this one:
Select
l.id AS "load.id",
l.brass_id AS "load.brass_id",
b.id AS "brass.id",
b.mfg_id AS "brass.mfg_id",
m.id AS "brass_mfg.id",
m.`name` AS "brass_mfg.name"
FROM `load` as l
LEFT JOIN brass as b ON l.brass_id = b.id
LEFT JOIN brass_mfg as m ON b.mfg_id = m.id
You need LEFT JOIN only

MySQL Join and Default

I'm still very new to SQL queries and can't quite figure this one out.
I have two tables, one table I'm running a general SELECT ... WHERE, super easy SQL statement.
Ex:
SELECT * from maindata where somedata4 LIKE "%data4.%"
This gives me back a list of all 6 entries below, however I want an additional column to show me if the current userdata.userId has a matching row and to include the amount column of that. If it doesn't have that row to default to a value of 0.
Table: maindata
id | somedata | somedata2 | somedata3 | somedata4
_________________________________________________
1 | data1.1 | data2.1 | data3.1 | data4.1
2 | data1.2 | data2.2 | data3.2 | data4.2
3 | data1.3 | data2.3 | data3.3 | data4.3
4 | data1.4 | data2.4 | data3.4 | data4.4
5 | data1.5 | data2.5 | data3.5 | data4.5
6 | data1.6 | data2.6 | data3.6 | data4.6
Table: userdata
id | itemId | amount | userId
_____________________________
1 | 6 | 4 | 1
2 | 4 | 4 | 26
3 | 4 | 2 | 1
It should search table maindata for WHERE somedata4 LIKE "%data4.%" and on each of those entries look in table userdata for userdata.amount with maindata.id = userdata.itemId WHERE maindata.userId = 1
Here's what I currently have for SQL
SELECT m.*, IFNULL(u.amount,0)
from maindata m
LEFT OUTER JOIN userdata u ON m.id = u.itemId
WHERE m.somedata4 LIKE "%data4.%"
What I'm missing is the filtering of only amounts from userdata.userId = 1, I want the entire list to show as it is in that query.
Expected Results:
id | somedata | somedata2 | somedata3 | somedata4 | amount
__________________________________________________________
1 | data1.1 | data2.1 | data3.1 | data4.1 | 0
2 | data1.2 | data2.2 | data3.2 | data4.2 | 0
3 | data1.3 | data2.3 | data3.3 | data4.3 | 0
4 | data1.4 | data2.4 | data3.4 | data4.4 | 4
5 | data1.5 | data2.5 | data3.5 | data4.5 | 0
6 | data1.6 | data2.6 | data3.6 | data4.6 | 2
SELECT m.*, IFNULL(u.amount,0) from maindata m LEFT OUTER JOIN
userdata u ON m.id = u.itemId WHERE m.userId = 1
Here is the SQL Query I was looking for (I think):
SELECT m.*, IFNULL(u.amount,0) AS "Amount"
FROM maindata m
LEFT OUTER JOIN userdata u ON m.id = u.itemId AND userid = 1
WHERE m.somedata4 LIKE "%data4.%"
It's giving me the desired results listed above, I just don't know if it's the most efficient way of handling this request.
SQLFiddle Here
I tried this solution but i am not sure if this is what you are looking for. Let me know.
-------EDIT--------
Now I'm sure about what you are looking for. I had just a couple of minutes so I didn't really optimized the code, but this seems to work except for the order of the fields, that can't be really modified since we're operating in two different sets of results.
By the way, this is the query
SELECT m.*, '0' as amount
from maindata m
left outer JOIN userdata u ON m.id = u.itemId
where (u.userid is null) and m.somedata4 LIKE '%data4.%'
UNION
SELECT m.*, u.amount
from maindata m
inner JOIN userdata u ON m.id = u.itemId
where u.userid = 1 and m.somedata4 LIKE '%data4.%'
and this is the updated fiddle
Hope this helps.

Performing a Join Query with Joins to rows that may not exist

I have the query:
SELECT `gigs`.*, COUNT(`signups`.`signupID`) AS `signupsPending` FROM `gigs` NATURAL JOIN `signups` WHERE (`signupStatus` = 4) GROUP BY `gigID`
That is querying a database that looks like this:
+-------+---------+------------+
| gigID | gigName | gigDate |
+-------+---------+------------+
| 1 | Foo | 01/01/2014 |
+-------+---------+------------+
| 2 | Bar | 16/01/2014 |
+-------+---------+------------+
+----------+-------+--------------+--------------+
| signupID | gigID | signedUserID | signupStatus |
+----------+-------+--------------+--------------+
| 1 | 1 | 1 | 1 |
+----------+-------+--------------+--------------+
| 2 | 1 | 2 | 4 |
+----------+-------+--------------+--------------+
| 3 | 1 | 3 | 2 |
+----------+-------+--------------+--------------+
| 4 | 2 | 1 | 2 |
+----------+-------+--------------+--------------+
But when I do the query above, it only shows a row for gigID = 1. How can I alter the above query so it will show 0 with the rest of the row?
Use LEFT OUTER JOIN instead of NATURAL JOIN.
SELECT `gigs`.*, COUNT(`signups`.`signupID`) AS `signupsPending`
FROM `gigs`
LEFT OUTER JOIN `signups`
WHERE (`signupStatus` = 4) GROUP BY `gigID`
I have been trying OUTER JOIN's, but that has not yielded any results, but I have now tried:
SELECT `gigs`.*, COUNT(`signups`.`signupID`) AS `signupsPending` FROM `gigs` LEFT JOIN `signups` ON `gigs`.`gigID` = `signups`.`gigID` AND `signupStatus` = 4 GROUP BY `gigID`
And that is working great
You want to use LEFT JOIN (to which you can add OUTER for ANSI-92 compatibility)
SELECT
gig.*
, COUNT(sig.signupID) AS "signupsPending"
FROM gigs AS gig
LEFT JOIN signups AS sig ON (gig.gigID = sig.gigID)
WHERE sig.signupStatus = 4
GROUP BY gig.gigID
Btw I advise you not to use NATURAL JOIN because it brings several risks
It's based on field names and you may have different field names as foreign keys
Different foreign keys can be used to JOIN 2 tables (even if you can add a USING statement)
During your database life tables can get field changed added, and you will always have to check that both tables don't get new fields with the same name
If you use both NATURAL and standard JOINs it can be difficult to automate your queries through scripts
...