Self-refering table. MySQL - mysql

I have another question. I don't seem to fully grasp the concept.
I have a table:
EMPLOYEES
| EMP_NO | APELLIDO | OFFICE | DIRECTOR | START_DATE | SALARY | COMMISSION | DEP_NO |
I have to obtain data of employees who's OFFICE is 'DIRECTOR' (easy, but... ) and I also have to include a column that would return full salary (salary+commission) of all the employees who are theirs subordinates.
Now I know that I need to 'copy' the table so I am able to make it to refer to itself (EMPLOYEES e1, EMPLOYEES e2 etc.). But I am getting really worked up in that obtaining total of salary of those subordinates.
Any thoughts?
(pls help)
EDIT
Sorry guys, true. My fault. OK, so here are some sample data:
+--------+----------+------------+----------+------------+---------+----------+--------+
| EMP_NO | SURNAME | OFFICE | DIRECTOR | START_DATE | SALARY |COMMISSION| DEP_NO |
+--------+----------+------------+----------+------------+---------+----------+--------+
| 7499 | ALONSO |SALESPERSON | 7698 | 1981-02-23 | 1400.00 | 400.00 | 30 |
| 7521 | LOPEZ | EMPLOYEE | 7782 | 1981-05-08 | 1350.50 | NULL | 10 |
| 7654 | MARTIN |SALESPERSON | 7698 | 1981-09-28 | 1500.00 | 1600.00 | 30 |
| 7698 | GARRIDO | DIRECTOR | 7839 | 1981-05-01 | 3850.12 | NULL | 30 |
| 7782 | MARTINEZ | DIRECTOR | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7839 | REY | CEO | NULL | 1981-11-17 | 6000.00 | NULL | 10 |
| 7844 | CALVO |SALESPERSON | 7698 | 1981-09-08 | 1800.00 | 0.00 | 30 |
| 7876 | GIL | ANALIST | 7782 | 1982-05-06 | 3350.00 | NULL | 20 |
| 7900 | JIMENEZ | EMPLOYEE | 7782 | 1983-03-24 | 1400.00 | NULL | 20 |
+--------+----------+------------+----------+------------+---------+----------+--------+
What I need to achieve now is to return table with details of employees GARRIDO and MARTINEZ (EMP_NO 7698 and 7782) along with additional column that would contain total salary of all theirs direct subordinates. Something like that:
+--------+----------+------------+----------+------------+---------+----------+--------+-----------+
| EMP_NO | SURNAME | OFICIO | DIRECTOR | FECHA_ALTA | SALARIO | COMISION | DEP_NO | TOTAL_EMP |
+--------+----------+------------+----------+------------+---------+----------+--------+-----------+
| 7698 | GARRIDO | DIRECTOR | 7839 | 1981-05-01 | 3850.12 | NULL | 30 | 6700 |
| 7782 | MARTINEZ | DIRECTOR | 7839 | 1981-06-09 | 2450.00 | NULL | 10 | 1350.50 |
+--------+----------+------------+----------+------------+---------+----------+--------+-----------+

I believe this should do it. You just need to JOIN to the other rows that you need, then it's a simple matter of grouping to get the aggregate amount that you want.
SELECT
D.emp_no,
D.apellido, -- Why is there one column named in Spanish and the rest in English?
D.office,
D.director,
D.start_date,
D.salary,
D.commission,
D.dep_no,
SUM(COALESCE(S.salary, 0) + COALESCE(S.commission, 0)) AS subordinates_compensation
FROM
Employees D
LEFT OUTER JOIN Employees S ON S.director = D.emp_no
WHERE
D.office = 'Director'
GROUP BY
D.emp_no,
D.apellido,
D.office,
D.director,
D.start_date,
D.salary,
D.commission,
D.dep_no

Related

Getting Empty Set in MySQL on using "not in"

I'm a beginner at MySQL. Due to this, there may be some errors. I have an employee department database for learning purposes. I have stored the supervisorENO for employees who have got a supervisor. If an employee does not have supervisor, their supervisorENO is null.
I have to retrieve the employees' name who is not supervisor. I ran the following command and got Empty set. I was not able to figure it out.
Here is my table:
MariaDB [EMP_DEPT]> select * from EMPLOYEE;
+-----+---------------+---------------------------+---------------+------------+------+------------+----------+
| Eno | Ename | Job_type | SupervisorENO | Hire_date | Dno | Commission | Salary |
+-----+---------------+---------------------------+---------------+------------+------+------------+----------+
| 111 | Aman Singh | HR Manager | NULL | 2000-01-23 | 50 | NULL | 5000.00 |
| 112 | Ankesh Kumar | HR Assistant | 111 | 2005-10-30 | 50 | NULL | 4000.00 |
| 113 | Gaurav Singh | Account Manager | NULL | 2002-07-09 | 60 | 100.00 | 6000.00 |
| 114 | Sanjeet Kumar | Accounting Clerk | 113 | 2015-04-18 | 60 | NULL | 4500.00 |
| 115 | Rajnish Yadav | Production Manager | NULL | 1980-12-04 | 10 | 150.00 | 5500.00 |
| 116 | Sumit Sharan | Production Incharge | 115 | 1995-02-24 | 10 | NULL | 4500.00 |
| 117 | Amartya Sinha | R&D Scientist | NULL | 2010-03-15 | 20 | NULL | 10000.00 |
| 118 | Shahnwaz Khan | R&D Associate Engineer | 117 | 2016-05-23 | 20 | NULL | 4000.00 |
| 119 | Sonu Giri | Purchase Executive | NULL | 2013-06-17 | 30 | 140.00 | 7000.00 |
| 120 | Kaushik Kumar | Purchase Specialist | 119 | 2018-08-13 | 30 | 4500.00 | 4000.00 |
| 121 | Vishal Yadav | Chief Marketing Officer | NULL | 1995-11-19 | 40 | 250.00 | 10000.00 |
| 122 | Satyam Jha | Digital Marketing Manager | 121 | 2004-09-29 | 40 | NULL | 4500.00 |
+-----+---------------+---------------------------+---------------+------------+------+------------+----------+
12 rows in set (0.001 sec)
MariaDB [EMP_DEPT]> select Ename from EMPLOYEE where Eno not in (select distinct SupervisorENO from EMPLOYEE);
Empty set (0.001 sec)
I am expecting the names of employees with Eno 112, 114, 116, 118, 120, 122 as they are not supervisor. Please help me in figure it out.
select distinct SupervisorENO from EMPLOYEE will also return NULL, any any value when compared to NULL will not actually return True or False, it will return unknown, so to speak. You can read more about that here for example.
So in order to fix your query, you need to exclude NULLs:
select Ename
from employee
where Eno not in (select distinct SupervisorENO
from employee
where supervisoreno is not null);
Here's a working demo on dbfiddle
You can simply query
select Ename from employee
where SupervisorEno is not null;
which will return those who have a supervisor, ie. are not themselves a supervisor.
select Ename from employee
where SupervisorEno is null;
Will return the supervisors.
It is better to avoid sub-queries when not strictly needed.

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.

Remove duplicate rows using MIN() function with GROUP BY and INNER JOIN

I am trying to list the unique/distinct MIN(time) for each person in the 'Results table' while joining the 'Athletes table' but I am getting duplicates.
Here is some sample data (I am running MySql 5.7)
Results Table
+----------+-----------+---------+----------+-------+-------------+------------+
| resultID | athleteID | eventID | ageGroup | time | venue | date |
+----------+-----------+---------+----------+-------+-------------+------------+
| 1 | 10 | 1 | MS | 10.20 | Tokyo | 06-06-2019 |
| 2 | 11 | 1 | MS | 10.24 | London | 03-08-2019 |
| 3 | 10 | 1 | MS | 10.20 | Los Angeles | 01-11-2019 |
| 4 | 13 | 1 | MS | 10.29 | Glasgow | 28-10-2019 |
| 5 | 14 | 1 | MS | 10.32 | Oslo | 16-07-2019 |
| ... | ... | ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... | ... | ... |
+----------+-----------+---------+----------+-------+-------------+------------+
Athletes Table
+-----------+-----------+----------+--------+-------------+
| athleteID | nameFirst | nameLast | gender | dateOfBirth |
+-----------+-----------+----------+--------+-------------+
| 10 | Bill | Smith | MS | 10-11-2000 |
| 11 | John | Brown | MS | 1-08-1999 |
| 12 | Steve | Jones | MS | 16-01-1997 |
| 13 | Alan | Green | MS | 21-07-2001 |
| 14 | Paul | Black | MS | 27-10-2000 |
| ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... |
+-----------+-----------+----------+--------+-------------+
I have tried the following code - which appears to bring the correct results set, but returns duplicate values. Bill Smith ran 10.20 twice but I only need to show one of them.
Have tried using the DISTINCT function on both SELECT's but no luck - so this is what I have:
SELECT *
FROM results
INNER JOIN (
SELECT athleteID, nameFirst, nameLast, MIN(time) as minTime
FROM results
INNER JOIN athletes USING(athleteID)
WHERE eventID = '1'
AND ageGroup IN('MS')
AND YEAR(results.date) = '2019'
GROUP BY athleteID
) AS child ON (results.athleteID = child.athleteID) AND (results.time = minTime)
HAVING YEAR(results.date) = '2019'
ORDER BY minTime ASC
I get this result
+-------+-----------+----------+-------------+------------+
| time | nameFirst | nameLast | venue | date |
+-------+-----------+----------+-------------+------------+
| 10.20 | Bill | Smith | Tokyo | 06-06-2019 |
| 10.20 | Bill | Smith | Los Angeles | 01-11-2019 |
| 10.24 | John | Brown | London | 03-08-2019 |
| 10.29 | Steve | Jones | Glasgow | 28-10-2019 |
| 10.32 | Alan | Green | Oslo | 16-07-2019 |
| ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... |
+-------+-----------+----------+-------------+------------+
As you can see, the additional result for Bill Smith (10.20 - Los Angeles) is also showing up. I need this to be omitted and only show 1 result per athlete - as below.
Desired Result
+-------+-----------+----------+---------+------------+
| time | nameFirst | nameLast | venue | date |
+-------+-----------+----------+---------+------------+
| 10.20 | Bill | Smith | Tokyo | 06-06-2019 |
| 10.24 | John | Brown | London | 03-08-2019 |
| 10.29 | Steve | Jones | Glasgow | 28-10-2019 |
| 10.32 | Alan | Green | Oslo | 16-07-2019 |
+-------+-----------+----------+---------+------------+
Any suggestions as to what I could try?
Many thanks in advance ..
You have athlete with the same min time in this case you need the min date too in outer select
SELECT r.athleteID, r.nameFirst, r.nameLast, min(r.date), child.minTime
FROM results r
INNER JOIN (
SELECT athleteID, nameFirst, nameLast
, MIN(time) as minTime
FROM results
INNER JOIN athletes USING(athleteID)
WHERE eventID = '1'
AND ageGroup IN('MS')
AND YEAR(results.date) = '2019'
GROUP BY athleteID
) AS child ON (r.athleteID = child.athleteID) AND (r.time = minTime)
WHERE YEAR(r.date) = '2019'
GROUP BY r.athleteID, child.minTime
ORDER BY minTime ASC

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

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.

Sql query related to my given tables

emp table
+-----+-------+-----+---------+
| eno | ename | dno | salary |
+-----+-------+-----+---------+
| 101 | sam | 1 | 1200.00 |
| 102 | ram | 1 | 1300.00 |
| 103 | alia | 1 | 2500.00 |
| 104 | tina | 2 | 1300.00 |
| 105 | avni | 2 | 1800.00 |
| 106 | suraj | 2 | 2000.00 |
| 107 | chris | 3 | 1500.00 |
| 108 | ben | 3 | 2000.00 |
| 109 | rina | 3 | 3300.00 |
+-----+-------+-----+---------+
Dept table
+-----+-------+-----------+
| dno | dname | location |
+-----+-------+-----------+
| 1 | csc | new delhi |
| 2 | phy | mumbai |
| 3 | chem | hyderabad |
+-----+-------+-----------+
I want to write a query
to display department no. ,department name, average salary of all department
Here dno Is foriegn key in emp table
Dno is a primary key in dept table
I tried many times but sometime there is an error and sometimes wrong output
My desire output is
+-----+-------+-------------+
| dno | dname | avg(salary) |
+-----+-------+-------------+
| 1 | csc | 1666.6666 |
| 2 | phy | 1700.0000 |
| 3 | chem | 2266.6666 |
+-----+-------+-------------+
This is classic 'group by' usage.
Try this:
select d.dno , d.dname, avg(salary)
from employee e LEFT JOIN department d on e.dno=d.dno
GROUP BY e.dno
Here is your query:
SELECT e.dno DNO, d.dname DNAME, AVG(e.salary) SALARY
FROM emp e, dept d
WHERE e.dno = d.dno
GROUP BY DNO