Joins with two tables conditionally based on the value of ONE column - mysql

I have following tables to manage purchase and issues of items.
item Table
+---------+-----------+
| item_id | item_name |
+---------+-----------+
| 500 | A4 |
| 501 | A5 |
| 502 | B5 |
| 503 | B4 |
| 504 | A3 |
+---------+-----------+
supplier Table
+-------------+---------------+
| sup_id | supplier_name |
+-------------+---------------+
| 1 | ABC Suppliers |
| 2 | DEF Suppliers |
| 3 | GHI Suppliers |
+-------------+---------------+
officer Table
+------------+--------------+
| officer_id | officer_name |
+------------+--------------+
| 1 | Jhon |
| 2 | William |
| 3 | Ken |
| 4 | Robert |
+------------+--------------+
purchase / issue table
+-----------+---------+---------+---------+------------+-----+-------------+
| update_id | bill_no | sup_id | item_id | date | qty | type |
+-----------+---------+---------+---------+------------+-----+-------------+
| 1000 | 10 | 1 | 500 | 2018-11-01 | 50 | purchase |
| 1001 | 40 | 1 | 500 | 2018-11-02 | 25 | purchase |
| 1002 | 32 | 2 | 500 | 2018-11-04 | 10 | issue |
| 1003 | 25 | 3 | 500 | 2018-11-05 | 12 | issue |
| 1004 | 14 | 1 | 500 | 2018-11-08 | 22 | purchase |
| 1005 | 55 | 2 | 501 | 2018-11-09 | 10 | purchase |
| 1006 | 30 | 2 | 502 | 2018-11-10 | 40 | purchase |
+-----------+---------+---------+---------+------------+-----+-------------+
02) purchase / issue table holds the purchase details and issue details by mentioning the "type" at the end of table.
03) I need to get the following output.
+-----------+------------------------------+------------+-----+------------+
| item_name | supplier_name / officer_name | date | qty |type |
+-----------+------------------------------+------------+-----+------------+
| A4 | ABC Suppliers | 2018-11-01 | 50 | purchase |
| A4 | ABC Suppliers | 2018-11-02 | 25 | purchase |
| A4 | William | 2018-11-04 | 10 | issue |
| A4 | Ken | 2018-11-05 | 12 | issue |
| A4 | ABC Suppliers | 2018-11-08 | 22 | purchase |
| A5 | DEF Suppliers | 2018-11-09 | 10 | purchase |
| B5 | DEF Suppliers | 2018-11-10 | 40 | purchase |
+-----------+------------------------------+------------+-----+------------+
03) I used the following query
select item.item_name,
supplier.supplier_name,
purchase.date,
purchase.qty,
purchase.type
from purchase
join item on item.item_id = purchase.item_id
where supplier.sup_id in (select sup_id from supplier)
04) But I did't get the desired output. I can not understand what I am going wrong. Can anyone help me ?

In your SELECT clause, you are referring to supplier table, without joining to that table. Now, it seems that you want to show officer_name when the purchase type is issue, else supplier_name when it is purchase.
We can do a LEFT JOIN to both the tables, and use CASE .. WHEN to determine the respective name.
I have removed the WHERE .. IN subquery, which does not seem to serve any purpose here.
select item.item_name,
CASE purchase.type
WHEN 'purchase' THEN supplier.supplier_name
WHEN 'issue' THEN officer.officer_name
END AS `supplier_name_officer_name`
purchase.date,
purchase.qty,
purchase.type
from purchase
join item on item.item_id = purchase.item_id
left join supplier on purchase.sup_id = supplier.sup_id
left join officer on purchase.sup_id = officer.officer_id

You need to join the supplier just as you did for the item.
And I see no point in the where clause at all.

Related

How to join two tables such that same column from both the tables are not repeated? [duplicate]

This question already has answers here:
MySQL Removing duplicate columns on Left Join, 3 tables
(4 answers)
Closed 2 years ago.
I have following two tables
Table 1: employee
+----+---------+--------+--------+------+-------+------+----------+
| No | Name | Salary | Zone | Age | Grade | Dept | HireDate |
+----+---------+--------+--------+------+-------+------+----------+
| 1 | Mukul | 30000 | West | 28 | A | 10 | NULL |
| 2 | Kritika | 35000 | Centre | 30 | A | 10 | NULL |
| 3 | Naveen | 35200 | West | 40 | B | 20 | NULL |
| 4 | Uday | 41800 | North | 38 | C | 30 | NULL |
| 5 | Nupur | 32000 | East | 26 | B | 20 | NULL |
| 6 | Moksh | 37000 | South | 28 | B | 10 | NULL |
| 7 | Shelly | 36000 | North | 26 | A | 30 | NULL |
+----+---------+--------+--------+------+-------+------+----------+
Table 2:department
+------+---------+--------+--------+------+
| Dept | Dname | Minsal | Maxsal | HoD |
+------+---------+--------+--------+------+
| 10 | Sales | 25000 | 32000 | 1 |
| 20 | Finance | 30000 | 50000 | 5 |
| 30 | Admin | 25000 | 40000 | 7 |
+------+---------+--------+--------+------+
Suppose I want to display details of all the employees who work in the Sales department.
I tried INNER JOIN
SELECT *
FROM employee AS A
INNER JOIN department AS B
ON A.Dept = B.Dept AND B.Dname = "Sales";
This displayed this table
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
| No | Name | Salary | Zone | Age | Grade | Dept | HireDate | Dept | Dname | Minsal | Maxsal | HoD |
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
| 1 | Mukul | 30000 | West | 28 | A | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
| 2 | Kritika | 35000 | Centre | 30 | A | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
| 6 | Moksh | 37000 | South | 28 | B | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
As you can see the Dept column is displayed twice.
So here are my questions
1. How to display same name columns only once?
2. Instead of INNER JOIN how else this can be done i.e Any better ways to do this?
Instead of selecting everything with * you need to specify the columns you want. You can also limit the * to all columns of a single table. Example:
SELECT e.*, d.Dname, d.HoD
FROM employee AS e
INNER JOIN department AS d ON e.Dept = d.Dept
WHERE d.Dname = "Sales";
Like this:
select A.No, A.Name, A.Salary, A.Zone, A.Age, A.Grade, A.Dept, A.HireDate,
B.Dname, B.Minsal, B.Maxsal, B.HoD
(...)
Inner join is the right way to join. No problems on this side.

MySQL select statement missing some fields

I have following statement that is used to select some fields from MySQL DB
select finance_budget_issue.budget_date, SUM(finance_budget_issue.amount) AS amount, finance_vote.office_id as vote_office_id, finance_office.office_head as head,
finance_office.office_name AS office_name,
finance_budget.ref_no, finance_budget_issue.view_status, tbl_signature.office_head as sign_office_head, tbl_signature.name AS name,
tbl_signature.post AS post, tbl_signature.sign_id
from finance_budget_issue
inner join finance_budget on finance_budget.budget_id=finance_budget_issue.budget_id
left join finance_vote on finance_budget_issue.vote_id=finance_vote.vote_id
left join finance_vote_description on finance_vote.description=finance_vote_description.vote_description_id
left join finance_office on finance_budget_issue.office=finance_office.office_id
left join tbl_signature on finance_office.office_id=tbl_signature.office_id
The statement is working fine, but didn't outs the following fields
tbl_signature.office_head as sign_office_head,
tbl_signature.name AS name,
tbl_signature.post AS post
What may be going wrong ? I think that I used incorrect Joins. Can anyone help ?
Tables as follows :
finance_office
+----+-----------+-------------+------+
| id | office_id | office_name | head |
+----+-----------+-------------+------+
| 1 | 48 | A | SS |
| 2 | 69 | B | VV |
+----+-----------+-------------+------+
finance_vote
+---------+-----------+----------------+
| vote_id | office_id | vote |
+---------+-----------+----------------+
| 1 | 48 | 320-1-2-1-1001 |
| 2 | 48 | 320-2-2-2-2002 |
| 3 | 69 | 319-1-2-1-1001 |
| 4 | 69 | 319-1-2-2-1102 |
| 5 | 30 | 318-1-1-2-1101 |
+---------+-----------+----------------+
tbl_signature
+---------+-----------+---------+------------+-------------+
| sign_id | office_id | name | post | office_head |
+---------+-----------+---------+------------+-------------+
| 1 | 48 | Noel | Accountant | Manager |
| 2 | 69 | Jhon | Accountant | Manager |
| 3 | 30 | Micheal | Accountant | Manager |
+---------+-----------+---------+------------+-------------+
finance_budget
+-----------+--------+-------------+
| budget_id | ref_no | budget_date |
+-----------+--------+-------------+
| 1 | Acc/01 | 2020-01-20 |
| 2 | Acc/02 | 2020-01-22 |
+-----------+--------+-------------+
finance_budget_issue
+----+-----------+--------+---------------+-----------------+
| id | budget_id | amount | budget_status | transfer_status |
+----+-----------+--------+---------------+-----------------+
| 1 | 1 | 75000 | issues | Approved |
| 2 | 1 | 22000 | issues | Approved |
| 3 | 2 | 65000 | issues | Approved |
+----+-----------+--------+---------------+-----------------+
Desired Output
+--------+----------------+------+--------+------------------+------+------------+
| amount | vote_office_id | head | ref_no | sign_office_head | name | post |
+--------+----------------+------+--------+------------------+------+------------+
| 75000 | 48 | SS | Acc/01 | Manager | Noel | Accountant |
| 22000 | 48 | SS | Acc/01 | Manager | Noel | Accountant |
| 65000 | 69 | VV | Acc/02 | Manager | Jhon | Accountant |
+--------+----------------+------+--------+------------------+------+------------+
Generated Output (Incorrect)
+--------+----------------+------+--------+------------------+------+------+
| amount | vote_office_id | head | ref_no | sign_office_head | name | post |
+--------+----------------+------+--------+------------------+------+------+
| 75000 | 48 | SS | Acc/01 | | | |
| 22000 | 48 | SS | Acc/01 | | | |
| 65000 | 69 | VV | Acc/02 | | | |
+--------+----------------+------+--------+------------------+------+------+
This is easier to read:
SELECT i.budget_date
, SUM(i.amount) amount
, v.office_id vote_office_id
, o.office_head head
, o.office_name
, b.ref_no
, i.view_status
, s.office_head sign_office_head
, s.name
, s.post
, s.sign_id
FROM finance_budget_issue i
JOIN finance_budget b
ON b.budget_id = i.budget_id
LEFT
JOIN finance_vote v
ON v.vote_id = i.vote_id
LEFT
JOIN finance_vote_description d
ON d.vote_description_id = v.description
LEFT
JOIN finance_office o
ON i.office = o.office_id
LEFT
JOIN tbl_signature s
ON s.office_id = o.office_id
You have an aggregate function (and non-aggregated columns) but no GROUP BY clause; that's not going to work. You have a LEFT JOINed table from which you select no columns; that's pointless.
For further help, see Why should I provide an MCRE for what seems to me to be a very simple SQL query

MySQL list all records in left table with Join

I have two tables, one includes vehicle data & other includes fuel data as follows:
tbl_vehicle
+------------+--------+
| vehicle_id | reg_no |
+------------+--------+
| 1 | ABC |
| 2 | DEF |
| 3 | GHI |
| 4 | JKA |
| 5 | LMN |
| 6 | OPQ |
+------------+--------+
tbl_direct_fuel
+---------+------------+----------+------------+
| fuel_id | vehicle_id | fuel_qty | date |
+---------+------------+----------+------------+
| 100 | 1 | 10 | 2019-10-01 |
| 101 | 1 | 12 | 2019-10-02 |
| 102 | 2 | 20 | 2019-10-03 |
| 103 | 3 | 15 | 2019-10-03 |
| 104 | 2 | 25 | 2019-10-04 |
+---------+------------+----------+------------+
I tried to get all records of left table with relevant records of right table. Used following Query.
select("reg_no,sum(fuel_qty) as total")
->from('tbl_direct_fuel')
->join('tbl_vehicle', 'tbl_direct_fuel.vehicle=tbl_vehicle.vehicle_id', 'left')
->group_by ('reg_no')
The above code shows only the following output.
+--------+----------+
| reg_no | total |
+--------+----------+
| ABC | 22 |
| DEF | 45 |
| GHI | 15 |
+--------+----------+
But I need all the vehicles with un-fueled vehicles as follows.
Desired output
+--------+----------+
| reg_no | total |
+--------+----------+
| ABC | 22 |
| DEF | 45 |
| GHI | 15 |
| JKA | 0 |
| LMN | 0 |
| OPQ | 0 |
+--------+----------+
You would to invert the tables in the left join, so that vehicules with no fueling do appear in the resultset (as it is, your left join allows for fueling without vehicules, which does not look like a relevant use case).
I would also recommend prefixing the column names with the table they come from to avoid ambiguity.
Finally, to return 0 (instead of null) for the vehicules that had no fueling, you can use coalesce().
select("tbl_vehicle.reg_no, coalesce(sum(fuel_qty), 0) fuel_qty")
->from('tbl_vehicle')
->join('tbl_direct_fuel', 'tbl_direct_fuel.vehicle = tbl_vehicle.vehicle_id', 'left')
->group_by ('tbl_vehicle.vehicle_id')
Try:
select t1.reg_no, t2.fuel_total
from tbl_vehicle t1
left join (
select vehicle_id, sum(fuel_qty) fuel_total
from tbl_direct_fuel
group by vehicle_id
) t2 on t1.vehicle_id = t2.vehicle_id

MySQL: Display query results in multiple columns based on value from a table

Would like to display query result from different tables:
Confirmed Orders: Confirmed Order Details:
+-------+--------+-------+ +--------+------+-------+------+
|COID |Customer|Payment| | CODID| COID | Items | Qty |
+-------+--------+-------+ +--------+------+-------+------+
| 1 | Jack | 11000 | | A | 1| Crack | 5 |
| 2 | Jill | 3000 | | B | 2| Weed | 7 |
+-------+--------+-------+ | C | 2| Beer | 7 |
+--------+------+-------+------+
Purchase Orders:
+--------+----------+---------+
| POID | PO Amount| Vendor |
+--------+----------+---------+
| 6 | 100 | Puffy.D |
| 7 | 500 | Bud Wise|
+--------+----------+---------+
And based on the values from a table named "DataMatrix":
DataMatrix:
+--------+--------------------+---------+--------------------------+
| DataID | table1 | DataID2 | table2 |
+--------+--------------------+---------+--------------------------+
| 1 | Confirmed Orders | A | Confirmed Order Details |
| 6 | Purchase Orders | A | Confirmed Order Details |
| 6 | Purchase Orders | 1T | Tracking |
| 2 | Confirmed Orders | B | Confirmed Order Details |
| 2 | Confirmed Orders | C | Confirmed Order Details |
| 7 | Purchase Orders | B | Confirmed Order Details |
| 7 | Purchase Orders | C | Confirmed Order Details |
| 7 | Purchase Orders | 2T | Tracking |
| 7 | Purchase Orders | 3T | Tracking |
+--------+--------------------+---------+--------------------------+
So hoping the result will be displayed into one table, such as below:
+--------+------+-------+-------+--------+-------+--------+----------+---------+
| CODID| COID | Items | Qty |Customer|Payment| POID | PO Amount| Vendor |
+--------+------+-------+-------+--------+-------+--------+----------+---------+
| A | 1| Crack | 5 | Jack | 11000 | 6 | 100 | Puffy.D
| B | 2| Weed | 7 | Jill | 3000 | 7 | 500 | Bud Wise|
| C | 2| Beer | 7 | Jill | 3000 | 7 | 500 | Bud Wise|
+--------+------+-------+-------+--------+-------+--------+----------+---------+
Self-taught MySQL, learned mostly from StackOverflow. I tried
max(case when (criteria) then...etc.
But the result can only be displayed in one column? Is what I am seeking even possible just using MySQL?

Complex MySQL query summing joined records where parent and child ids are equal

Maybe this will be an easy one for some of you MySQL masters who see this stuff like a level 3 children's book.
I have multiple tables that I'm joining to produce statistical data for a report and I'm getting tripped up at the moment trying to figure it out. It's obviously imperative the figures are correct because it impacts a number of decisions going forward.
Here's the lay of the land (not the full picture, but you'll get the point):
Affiliate Table
+----+-----------+------------+---------------------+
| id | firstname | lastname | created_date |
+----+-----------+------------+---------------------+
| 1 | Mike | Johnson | 2010-11-22 17:44:37 |
| 2 | Trevor | Wilson | 2010-12-23 16:24:24 |
| 3 | Bob | Parker | 2011-11-04 10:33:49 |
+----+-----------+------------+---------------------+
Now our query should only find results for Bob Parker (id 3) so I'll only show example results for Bob.
Affiliate Link Table
+-----+-----------+--------------+-----------+----------+---------------------+
| id | parent_id | affiliate_id | link_type | linkhash | created_date |
+-----+-----------+--------------+-----------+----------+---------------------+
| 21 | NULL | 3 | PRODUCT | fa2e82a7 | 2011-06-15 16:18:37 |
| 27 | NULL | 3 | PRODUCT | 55de2ae7 | 2011-06-23 01:03:00 |
| 28 | NULL | 3 | PRODUCT | 02cae72f | 2011-06-23 01:03:00 |
| 29 | 27 | 3 | PRODUCT | a4dfb2c8 | 2011-06-23 01:03:00 |
| 30 | 28 | 3 | PRODUCT | 72cea1b2 | 2011-06-23 01:03:00 |
| 36 | 21 | 3 | PRODUCT | fa2e82a7 | 2011-06-23 01:07:03 |
| 59 | 21 | 3 | PRODUCT | ec33413f | 2011-11-04 17:49:17 |
| 60 | 27 | 3 | PRODUCT | f701188c | 2011-11-04 17:49:17 |
| 69 | 21 | 3 | PRODUCT | 6dfb89fd | 2011-11-04 17:49:17 |
+-----+-----------+--------------+-----------+----------+---------------------+
Affiliate Stats
+--------+--------------+--------------------+----------+---------------------+
| id | affiliate_id | link_id | order_id | type | created_date |
+--------+--------------+---------+----------+----------+---------------------+
| 86570 | 3 | 21 | NULL | CLICK | 2013-01-01 00:07:31 |
| 86574 | 3 | 21 | NULL | PAGEVIEW | 2013-01-01 00:08:53 |
| 86579 | 3 | 21 | 411 | SALE | 2013-01-01 00:09:52 |
| 86580 | 3 | 36 | NULL | CLICK | 2013-01-01 00:09:55 |
| 86582 | 3 | 36 | NULL | PAGEVIEW | 2013-01-01 00:09:56 |
| 86583 | 3 | 28 | NULL | CLICK | 2013-01-01 00:11:04 |
| 86584 | 3 | 28 | NULL | PAGEVIEW | 2013-01-01 00:11:04 |
| 86586 | 3 | 30 | NULL | CLICK | 2013-01-01 00:30:18 |
| 86587 | 3 | 30 | NULL | PAGEVIEW | 2013-01-01 00:30:20 |
| 86611 | 3 | 69 | NULL | CLICK | 2013-01-01 00:40:19 |
| 86613 | 3 | 69 | NULL | PAGEVIEW | 2013-01-01 00:40:19 |
| 86619 | 3 | 69 | 413 | SALE | 2013-01-01 00:42:12 |
| 86622 | 3 | 60 | NULL | CLICK | 2013-01-01 00:46:00 |
| 86624 | 3 | 60 | NULL | PAGEVIEW | 2013-01-01 00:46:01 |
| 86641 | 3 | 60 | NULL | PAGEVIEW | 2013-01-01 00:55:58 |
| 86642 | 3 | 30 | 415 | SALE | 2013-01-01 00:56:35 |
| 86643 | 3 | 28 | NULL | PAGEVIEW | 2013-01-01 00:56:43 |
| 86644 | 3 | 60 | 417 | SALE | 2013-01-01 00:56:52 |
+--------+--------------+---------+----------+----------+---------------------+
Orders
+------+--------------+---------+---------------------+
| id | affiliate_id | total | created_date |
+------+--------------+---------+---------------------+
| 411 | 3 | 138.62 | 2013-01-01 00:09:50 |
| 413 | 3 | 312.87 | 2013-01-01 00:09:52 |
| 415 | 3 | 242.59 | 2013-01-01 00:09:55 |
| 417 | 3 | 171.18 | 2013-01-01 00:09:55 |
+------+--------------+---------+---------------------+
Now the results that I need should look like this (only show main/parent link id)
+---------+---------+
| link_id | total |
+---------+---------+
| 21 | 451.49 | <- 1 order from parent (21), 1 from child (69)
| 27 | 171.18 | <- 1 order from child (69)
| 28 | 242.59 | <- 1 order from child (30)
+---------+---------+
I'm not quite sure how to write the query so that I can sum where affiliate_link.id and affiliate_link.parent_id are combined. Is this even possible with a couple of JOINs and GROUPing?
I'm not too sure why you have denormalised affiliate_id (by placing it in each table) and, therefore, whether one can rely on all Stats and Orders that stem from a particular Link to have the same affiliate_id as that Link.
If it's possible, I'd suggest changing the AffiliateLink.parent_id column such that parent records point to themselves (rather than NULL):
UPDATE AffiliateLink SET parent_id = id WHERE parent_id IS NULL
Then it's a simple case of joining and grouping:
SELECT AffiliateLink.parent_id AS link_id,
SUM(Orders.total) AS total
FROM AffiliateLink
JOIN AffiliateStats ON AffiliateStats.link_id = AffiliateLink.id
JOIN Orders ON Orders.id = AffiliateStats.order_id
WHERE AffiliateLink.affiliate_id = 3
GROUP BY AffiliateLink.parent_id
See it on sqlfiddle.
If it's not possible to make the change, you can effectively create the resulting AffiliateLink table using UNION (but beware the performance implications, as MySQL will not be able to use indexes on the result):
(
SELECT parent_id, id, affiliate_id FROM AffiliateLink WHERE parent_id IS NOT NULL
UNION ALL
SELECT id , id, affiliate_id FROM AffiliateLink WHERE parent_id IS NULL
) AS AffiliateLink
See it on sqlfiddle.