Joining on several columns - mysql

I have a database which allows to keep all exchanges of products between different stores. Here are the tables :
Delivery table:
+-------------+-----------+----------------+---------+
| delivery_id | origin_id | destination_id | ongoing |
+-------------+-----------+----------------+---------+
| 15 | 1 | 2 | 1 |
| 16 | 3 | 4 | 0 |
+-------------+-----------+----------------+---------+
Site table:
+---------+------------+----------------+
| site_id | name | address |
+---------+------------+----------------+
| 1 | site_1 | ... |
| 2 | site_2 | ... |
+-------------+--------+----------------+
The origin and destination ids are foreign keys referring to site_id.
My goal is to list all the ongoing deliveries with the names of origin and destination stores. For the example given above, the expected result would be:
+---------+------------+
| origin |destination |
+---------+------------+
| site_1 | site_2 |
+---------+------------+
For the moment, the only way i have found to select the destination and origin ids with the associated delivery id and then joining these two results:
SELECT origin.origin, destination.destination FROM
(SELECT site.name AS origin,delivery.delivery_id FROM delivery INNER JOIN site on site.site_id=delivery.origin_id WHERE delivery.ongoing=1) origin
INNER JOIN (SELECT site.name as destination,delivery.delivery_id FROM delivery INNER JOIN site ON site.site_id=delivery.destination_id WHERE delivery.ongoing=1) destination
ON origine.code=destination.code
I am pretty sure there is an easier and more efficient way to do that but I cannot find it. Can anybody confirm?

You seem to just want two joins:
select so.name as origin_name, sd.name as destination_name
from deliveries d left join
sites so
on d.origin_id = so.site_id left join
sites sd
on d.destination_id = sd.site_id
where d.ongoing = 1;
This uses left join just in case one of the columns in delivery has a NULL value. That actually seems unlikely in this case, so the outer join is probably not necessary.

Related

How to join two table to display sales for each id

I was trying to display the total sale of each id by combining the two table by using the id. I have two table 1. user table, 2. sales table
//user table
--------------
| id | name |
---------------
| 1 | yuki |
| 2 | soman |
---------------
// sales table
--------------
| id | total|
---------------
| 1 | 300 |
| 2 | 23 |
| 1 | 500 |
---------------
With my query it only display 1 sale witch is sales for yuki.
SELECT i.name,SUM(ROUND(s.total),2)) AS sales
FROM user i
INNER JOIN sales s
ON i.id = s.id
--------------
| name | sales|
---------------
| yuki | 800 |
---------------
I want to display the output like this, what did I missed from my query?
--------------
| name | sales|
---------------
| yuki | 800 |
|soman | 23 |
---------------
Your query needs a group by clause:
SELECT u.name, SUM(ROUND(s.total),2)) AS sales
FROM user u
INNER JOIN sales s ON s.id = u.id
GROUP BY u.id, u.name
Such error is much easier to spot when sql mode ONLY_FULL_GROUP_BY is enabled.
As an alternative, you might want to consider a correlated subquery, which avoids outer aggregation (it actually behaves like a LEFT JOIN, which is - probably - closer to what you want):
SELECT u.*,
(SELECT SUM(ROUND(s.total),2)) FROM sales s WHERE s.id = u.id) AS sales
FROM user u
Side note: user is a language keyword, hence not a good choice for a column name. Consider using something else, such as users for example.

Querying from multiple tables when the second table has multiple conditions that should match

I am trying to write a MySQL query, which fetches data from two different connected tables. I already stumbled upon a simmilar situation some times ago, and some Stackoverflower helped me out with it, but now, trying to start from his solution, I can't figure out how should I modify the query for my current problem.
Consider that I have these two tables:
Transactions
-------------------------------------
| TransactionId | Currency | Amount |
-------------------------------------
| 1 | EUR | 15.00 |
| 2 | EUR | 39.50 |
| 3 | USD | 10.00 |
-------------------------------------
and
Transaction_Paths
---------------------------------------------------------------------------------
| TPathId | TPathType | TPathDirection | TPathLink | transactions_TransactionId |
---------------------------------------------------------------------------------
| 1 | 'bank' | 'to' | 1 | 1 |
| 2 | 'bank' | 'from' | 2 | 1 |
| 3 | 'account' | 'from' | 1 | 1 |
| 4 | 'account' | 'to' | 2 | 1 |
| 5 | 'bank' | 'to' | 3 | 2 |
| 6 | 'bank' | 'from' | 2 | 2 |
| 7 | 'bank' | 'to' | 4 | 3 |
| 8 | 'account' | 'from' | 1 | 3 |
---------------------------------------------------------------------------------
Basically, these two tables are storing transactions happening on my site, with information about those transactions in the Transaction_Paths table. Each line in the Transactions table may have multiple attached lines in the Transaction_Paths table, via the Transaction_paths.transactions_TransactionId field.
How could I write a query, which queries out the exact transaction which is coming 'from' the account with the id of 1, and going to the bank with the id of 4?
Based on the previous question that I asked, and the answer to it, I should be using HAVING, but how can I use having, when I have multiple conditions that should match on the second table?
EDIT: The expected result should be to return only the third line from the Transactions table, as in the line, with the TransactionId of 3.
To achieve the desire output you need to make a three cascading left join Transaction_Paths Left join Transaction_Paths left join Transactions. The first left join (the self join Transaction_Paths Left join Transaction_Paths ) will be with no condition , but the second left join will be on (Transaction_Paths.transactions_TransactionId = Transaction_Paths.TransactionId).
I have made a pseudo query for this to give you an idea as following:
SELECT c.*
FROM Transaction_Paths a
LEFT JOIN Transaction_Paths b
LEFT JOIN Transactions c ON (a.transactions_TransactionId = c.TransactionId)
WHERE a.TPathDirection = "from" AND a.TPathLink = 1 AND b.TPathDirection = "to" AND b.TPathLink = 4
Hope you can come up with the appropriate query using your favorite query console. Or share your schema with a fiddle (e.g., SQL fiddle) and I will try to build the exact query and edit this post.
In the meantime, I totally forgot about M H Rasel's answer, and found another way to do it. For the effort put in by the answerer, I will mark his answer as the accepted one, and in the meanwhile, someone can comment on my answer if this will indeed work in every situation? My tests, altough limited, provided the correc results:
SELECT Transactions.*
FROM Transactions
INNER JOIN Transaction_Paths
ON Transaction_Paths.transactions_TransactionId = Transactions.TransactionId
WHERE Transaction.Currency = 'USD' AND
((TransactionPaths.TPathDirection = 'from' AND TransactionPaths.TPathLink = '1')
OR (TransactionPaths.TPathDiretion= 'to' AND TransactionPaths.TPathLink = '4'))
GROUP BY Transactions.TransactionId
HAVING COUNT(distinct Transaction_Paths.TPathId) = 2

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

INNER JOIN ? outcomes on two columns

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

Selecting other side of many to many relationship

Background
I have three tables: (SQL fiddle: http://sqlfiddle.com/#!2/f7b33/11)
products
+----+-----------+
| id | product |
+----+-----------+
| 1 | product_1 |
| 2 | product_2 |
| 3 | product_3 |
+----+-----------+
products_features
+------------+------------+
| product_id | feature_id |
+------------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 3 |
| 3 | 4 |
+------------+------------+
features
+----+-----------+
| id | feature |
+----+-----------+
| 1 | feature_1 |
| 2 | feature_2 |
| 3 | feature_3 |
| 4 | feature_4 |
+----+-----------+
I'm then selecting like this:
SELECT products.product,
GROUP_CONCAT(features.feature) AS features
FROM products
LEFT JOIN products_features
ON product_id = products.id
LEFT JOIN features
ON products_features.feature_id = features.id
GROUP BY products.id
to get something like:
+-----------+-----------------------------------------+
| product | features |
+-----------+-----------------------------------------+
| product_1 | feature_1,feature_2,feature_3,feature_4 |
| product_2 | feature_1,feature_3 |
| product_3 | feature_4 |
+-----------+-----------------------------------------+
Question
So, everything is great, However, what I'd like to do is to only have things that have feature_1 and feature_3, whilst still getting the other features.
In other words, I'd like to write a query that would get me:
+-----------+-----------------------------------------+
| product | features |
+-----------+-----------------------------------------+
| product_1 | feature_1,feature_2,feature_3,feature_4 |
| product_2 | feature_1,feature_3 |
+-----------+-----------------------------------------+
I've tried:
SELECT products.product,
GROUP_CONCAT(features.feature) AS features
FROM products
LEFT JOIN products_features
ON product_id = products.id
RIGHT JOIN features
ON products_features.feature_id = features.id AND features.feature in ('feature_1','feature_3')
GROUP BY products.id
but of course I get:
+-----------+---------------------+
| product | features |
+-----------+---------------------+
| (null) | feature_4,feature_2 |
| product_1 | feature_1,feature_3 |
| product_2 | feature_3,feature_1 |
+-----------+---------------------+
So though I now know product_1 and product_2 are the ones with those features, I can't see the rest of the features they have.
What query will give me allow me to specify feature_1 and feature_3 and get the following response?
+-----------+-----------------------------------------+
| product | features |
+-----------+-----------------------------------------+
| product_1 | feature_1,feature_2,feature_3,feature_4 |
| product_2 | feature_1,feature_3 |
+-----------+-----------------------------------------+
I think the most generalizable way to approach this is with a having clause:
SELECT products.product,
GROUP_CONCAT(features.feature) AS features
FROM products
LEFT JOIN products_features
ON product_id = products.id
LEFT JOIN features
ON products_features.feature_id = features.id
GROUP BY products.id
HAVING sum(features.feature = 'feature_1') > 0 and
sum(features.feature = 'feature_3') > 0;
Each clause in the having statement is counting the number of times that a given feature appears. The and is requiring that both features be in the final result set.
EDIT:
Given the structure of your statement, you could also do:
HAVING find_in_set('feature_1', features) > 0 and
find_in_set('feature_3', features) > 0;
This works because you are producing a column with the list of features and you are using a comma as a separator for that list.
Returning data from a group_concat and then splitting the results back in your front-end is a no go. Not only will it result in inefficient use of resources but also might result in errors in the splitting (eg: imagine what would happen when a feature appears and it happens to contain a ,).
The third and main reason not to use a group_concat is that it has a limit on the length. I'm not re-explaining the wheel but check this question for more information.
The best approach will be to return all the matching features for a given product and then just process them in a loop. It should be pretty simple to do (actually, most UI components, web or not, would expect to receive a collection to display them and this is what you're sending to them).
Additionally, it is most likely that you already have the ids of the features that you want to check so it would be more efficient to check for them instead of strings.
My last comment is that you don't actually need left joins in there. A left join will return all elements from the left regardless of whether they have a match in the right. However, you need the right side to have 2 elements (the 2 features) which makes the query contradictory. You just need an inner join in there.
This is the query I would use:
SELECT p.product, f.feature FROM products p
JOIN (
SELECT product_id FROM products_features
WHERE feature_id IN (1, 3)
GROUP BY product_id
HAVING count(*) = 2
) pf ON p.id = pf.product_id
JOIN products_features pf2 ON p.id = pf2.product_id
JOIN features f ON pf2.feature_id = f.id
This is the fiddle for that query.