How to substitute the column values with a MySQL query - mysql

I am working with MySQL Workbench to get the table I am looking for. I am almost there. Here is the result of the query:
----------------------------------------------------------------------------
employee.manager_id | employee.id | employee.first_name | employee.last_name
----------------------------------------------------------------------------
null | 1 | Petra | Wallace
null | 3 | Peter | Willis
null | 5 | Michael | Best
1 | 2 | David | Lone
3 | 4 | Barbara | Grinder
5 | 6 | Anthony | Krone
Now, I want to replace the values of the column employee.manager_id with the following:
When the value is null, either leave it null or substitute it with the string "none"
When the value has a number, it references the number of the employee.id. For example, the value 1 in employee.manager_id represents employee.id number 1, who is Petra Wallace.
I would like to show in the employee.manager_id column, the employee.first_name and the employee.last_name, instead of a number. Anybody has any idea how to do it?

A left join retrieves a manager based on the JOIN criteria. If there isn't a manager, then all of the manager.* fields are NULL.
SELECT
COALESCE(manager.first_name,"none") as manager_first_name,
manager.last_name as manager_last_name,
employee.id,
employee.first_name,
employee.last_name
FROM employee
LEFT JOIN employee manager
ON employee.manager_id = manager.id
ORDER BY employee.manager_id

Try this:
SELECT
CASE WHEN employee.manager_id IS NULL THEN "NONE"
ELSE (SELECT CONCAT(e.first_name, ' ', e.last_name), e.id FROM employee e
WHERE e.id = employee.manager_id)
END AS case_generated_column,
employee.id, employee.first_name, employee.last_name FROM employee ORDER BY employee.manager_id;

Related

MYSQL subquery results columns not as expected

I am trying to build a query that will build a output like this.
+-----------------+--------------+
| Name | patient appt |
+-----------------+--------------+
| Dharmick Ketan | 75 |
| See Ka Wai | 45 |
| Totoritis Susan | 25 |
| Seay Duval | 147 |
+-----------------+--------------+
The output that I go is this
+-----------------+--------------+
| Name | patient appt |
+-----------------+--------------+
| Dharmick Ketan | patient appt |
| See Ka Wai | patient appt |
| Totoritis Susan | patient appt |
| Seay Duval | patient appt |
+-----------------+--------------+
I was following the instructions here https://www.mysqltutorial.org/mysql-subquery/
My query is this
mysql> select concat(p.lname,' ' , p.fname) as 'Name',
-> 'patient appt'
-> from patient_data as p
-> where
-> p.financial_review_exp>'2019-07-01'
-> and
-> 'patient appt' = ( select count(*) from openemr_postcalendar_events e
-> where e.pc_pid = p.pid );
What I was expecting to happen was the alias 'patient appt' to be populated with the data from the nested select statement. I was thinking this would work because I am trying to produce multiple columns that are going to be populated by subqueries. All of the columns are counts of appointments in the calendar table.
So, does the 'patient appt' need to be a column in the patient_data table? If so, is there another way to produce the desired column data?
Put the correlated subquery directly in the select clause:
select concat(p.lname,' ' , p.fname) as name,
(select count(*) from openemr_postcalendar_events as e where e.pc_pid = p.pid) as patient_appt
from patient_data as p
where p.financial_review_exp > '2019-07-01'
Note: don't use single quotes for column aliases! They are meant for literal strings only. In MySQL, you can use backticks to quote identifiers - but better yet, use identifiers that do not require quoting, so you don't have to worry about that.

Mysql SELECT display only 1 result instead of multiple from 3 tables

I try to make one SQL request where I get all data from multiple tables. Problem start when one user have for example more "enforcement". But first lets look on code:
SELECT c.id, c.firstname, c.surname, c.email, c.process, c.search_work, c.note,
MAX(CASE WHEN cl.languageID = 1 THEN cl.skill ELSE '-' END)AS 'en',
MAX(CASE WHEN cl.languageID = 2 THEN cl.skill ELSE '-' END)AS 'ge',
ce.enforcement
FROM candidates AS c
LEFT JOIN candidates_language AS cl ON c.id = cl.candidates_id
LEFT JOIN candidates_enforcement as ce on c.id = ce.candidates_id
GROUP BY c.id, c.firstname, c.surname, c.email
As you can see from here I search above multiple tables with using foreign key on candidates ID.
For this purpouse here is how 2 tables looks like:
candidates
------------------------------------------------------------------------
| id | firstname | surname | email |
------------------------------------------------------------------------
| 22 | John | Doe | john#doe.com |
------------------------------------------------------------------------
| 23 | Peter | Miller | doe#john.com |
------------------------------------------------------------------------
candidates_enforcement
--------------------------------------------------
| id | candidates_id | enforcement |
--------------------------------------------------
| 1 | 22 | Advocate |
--------------------------------------------------
| 2 | 22 | Programmer |
--------------------------------------------------
| 3 | 23 | IT Admin |
--------------------------------------------------
candidates_id = foreign key from candidates. With my SQL request above result should looks like:
---------------------------------------------------------------------------------
| id | firstname | surname | email | enforcement
---------------------------------------------------------------------------------
| 22 | John | Doe | john#doe.com | Advocate, Programmer |
---------------------------------------------------------------------------------
| 23 | Peter | Miller | doe#john.com | IT Admin
---------------------------------------------------------------------------------
Unfortunately it display me ALWAYS only 1 result from "enforcement". So for cancidate with id 22 it is Advocate not Advocate, Programmer
Is there a chance someone can help me to find a way how to fix this?
Thanks
p.s. Working demo on FIDDLE
http://sqlfiddle.com/#!9/25b1b/1
You could use GROUP_CONCAT like this:
SELECT
candidates.id,
candidates.firstname,
candidates.surname,
candidates.email,
group_concat(DISTINCT candidates_enforcement.enforcement)
FROM
candidates
LEFT JOIN candidates_enforcement
ON candidates.id = candidates_enforcement.candidates_id
GROUP BY
candidates.id,
candidates.firstname,
candidates.surname,
candidates.email
Reference:
GROUP_CONCAT(expr)
You can use Group_Concat with the Distinct option
SELECT c.id,c.firstname,c.surname,c.email,group_concat(distinct ce.enforcement)
FROM candidates c
LEFT JOIN candidates_enforcement ce
ON c.id=ce.candidates_id
GROUP BY c.id,c.firstname,c.surname,c.email
The distinct option will help you remove off the redundant values.
As I understood you want both enforcement to show up when a person has two enforcement. The problem here is you are LEFT joining the candidates_enforcement table to candidate table.
LEFT join does is get the tuples from candidate table and joins the corresponding tuples from candidates_enforcement. There is only one tuple for candidate in candidate table. So it only shows up one time whether candidates_enforcement has many tuples for that particular candidate or not.
To correct this do a RIGHT JOIN. Or you can do the same LEFT JOIN, with tables swapped.
SELECT c.id, c.firstname, c.surname, c.email,ce.enforcement
FROM candidates AS c
RIGHT JOIN candidates_enforcement as ce on c.id = ce.candidates_id

SQL sorting does not follow group by statement, always uses primary key

I have a SQL database with a table called staff, having following columns:
workerID (Prim.key), name, department, salary
I am supposed to find the workers with the highest salary per department and used the following statement:
select staff.workerID, staff.name, staff.department, max(staff.salary) AS biggest
from staff
group by staff.department
I get one worker shown from each department, but they are NOT the workers with the highest salary, BUT the biggest salary value is shown, even though the worker does not get that salary.
The person shown is the worker with the "lowest" workerID per department.
So, there is some sorting going on using the primary key, even though it is not mentioned in the group by statement.
Can someone explain, what is going on and maybe how to sort correctly.
Explanation for what is going on:
You are performing a GROUP BY on staff.department, however your SELECT list contains 2 non-grouping columns staff.workerID, staff.name. In standard sql this is a syntax error, however MySql allows it so the query writers have to make sure that they handle such situations themselves.
Reference: http://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
In standard SQL, a query that includes a GROUP BY clause cannot refer to nonaggregated columns in the select list that are not named in the GROUP BY clause.
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause.
The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate.
Starting with MySQL 5.1 the non-standard feature can be disabled by setting the ONLY_FULL_GROUP_BY flag in sql_mode: http://dev.mysql.com/doc/refman/5.6/en/sql-mode.html#sqlmode_only_full_group_by
How to fix:
select staff.workerID, staff.name, staff.department, staff.salary
from staff
join (
select staff.department, max(staff.salary) AS biggest
from staff
group by staff.department
) t
on t.department = staff.department and t.biggest = staff.salary
In the inner query, fetch department and its highest salary using GROUP BY. Then in the outer query join those results with the main table which would give you the desired results.
This is the usual case group by with a aggregate function does not guarantee proper row corresponding to the aggregate function. Now there are many ways to do it and the usual practice is a sub-query and join. But if the table is big then performance wise it kills, so the other approach is to use left join
So lets say we have the table
+----------+------+-------------+--------+
| workerid | name | department | salary |
+----------+------+-------------+--------+
| 1 | abc | computer | 400 |
| 2 | cdf | electronics | 200 |
| 3 | gfd | computer | 400 |
| 4 | wer | physics | 300 |
| 5 | hgt | computer | 700 |
| 6 | juy | electronics | 100 |
| 7 | wer | physics | 400 |
| 8 | qwe | computer | 200 |
| 9 | iop | electronics | 800 |
| 10 | kli | physics | 800 |
| 11 | qsq | computer | 600 |
| 12 | asd | electronics | 300 |
+----------+------+-------------+--------+
SO we can get the data as
select st.* from staff st
left join staff st1 on st1.department = st.department
and st.salary < st1.salary
where
st1.workerid is null
The above will give you as
+----------+------+-------------+--------+
| workerid | name | department | salary |
+----------+------+-------------+--------+
| 5 | hgt | computer | 700 |
| 9 | iop | electronics | 800 |
| 10 | kli | physics | 800 |
+----------+------+-------------+--------+
My favorite solution to this problem uses LEFT JOIN:
SELECT m.workerID, m.name, m.department, m.salary
FROM staff m # 'm' from 'maximum'
LEFT JOIN staff o # 'o' from 'other'
ON m.department = o.department # match rows by department
AND m.salary < o.salary # match each row in `m` with the rows from `o` having bigger salary
WHERE o.salary IS NULL # no bigger salary exists in `o`, i.e. `m`.`salary` is the maximum of its dept.
;
This query selects all the workers that have the biggest salary from their department; i.e. if two or more workers have the same salary and it is the bigger in their department then all these workers are selected.
Try this:
SELECT s.workerID, s.name, s.department, s.salary
FROM staff s
INNER JOIN (SELECT s.department, MAX(s.salary) AS biggest
FROM staff s GROUP BY s.department
) AS B ON s.department = B.department AND s.salary = B.biggest;
OR
SELECT s.workerID, s.name, s.department, s.salary
FROM (SELECT s.workerID, s.name, s.department, s.salary
FROM staff s
ORDER BY s.department, s.salary DESC
) AS s
GROUP BY s.department;

MySQL - Reshape Data

I have the following data set (sample):
emplid | Citizeship |
100001 | USA |
100001 | CAN |
100001 | CHN |
100002 | USA |
100002 | CHN |
100003 | USA |
Is there a way to transform the data into the following:
emplid | Citizeship_1 | Citizenship_2 | Citizenship_3
100001 | USA | CHN | CAN
100002 | USA | CHN |
100003 | USA | |
The assumption is that each emplid will have up to 4 citizenships.
I started with the following codes, but for the emplids who just have 1 citizenship, the value is being repeated in the citizenship_2, citizenship_3, which should be just blank:
select *
, substring_index(Citizenship_multiple, ',', 1) as Citizenship_1
, substring_index(substring_index(Citizenship_multiple,',',-1),',',1) as Citizenship_2
, substring_index(substring_index(Citizenship_multiple,',',-2),',',1) as Citizenship_3
, substring_index(substring_index(Citizenship_multiple,',',-3),',',1) as Citizenship_4
from
(select *
, group_concat(distinct Citizenship) as Citizenship_multiple
from `citizenship_csv_meta`
group by emplid) a
you can do it with case and max
SELECT emplid,
max(case when Citizeship = 'USA' then 'USA' else '' end) as Citizeship_1,
max(case when Citizeship = 'CHN' then 'CHN' else '' end) as Citizeship_2,
max(case when Citizeship = 'CAN' then 'CAN' else '' end) as Citizeship_3
FROM citizenship_csv_meta
GROUP BY emplid
I know you stated hardcoding was a pain, and likely not the best solution, but I was able to do this while using only one assumption: that an employee can have at most 4 citizenships. So, I just joined your table together 4 times. I had to use an outer join, because not every employee would have 4 citizenships. Here is the code, and I will explain what I did:
SELECT e.emplid, MAX(e.citizenship) AS citizenship1,
MAX(e1.citizenship) AS citizenship2,
MAX(e2.citizenship) AS citizenship3,
MAX(e3.citizenship) AS citizenship4
FROM employee e
LEFT JOIN employee e1 ON e1.emplid = e.emplid AND e1.citizenship < e.citizenship
LEFT JOIN employee e2 ON e2.emplid = e1.emplid AND e2.citizenship < e1.citizenship
LEFT JOIN employee e3 ON e3.emplid = e2.emplid AND e3.citizenship < e2.citizenship
GROUP BY e.emplid
I joined your table together 4 times, and took the MAX() citizenship from each group. The reason this works is because in the join condition i used e1.citizenship < e.citizenship to make sure that the previous values weren't included. For example, table e2 never included USA, so I was able to use the max function again.
What this will do is that once an employee no longer has a citizenship, the cell in the remaining columns is null, so you will need to be aware of that.
This tested beautifully on SQL Fiddle, and I actually referenced this question to figure out how to get the succeeding citizenships. Of course, I used a method slightly different from theres, but I want to give credit where credit is due.
EDIT
If you want the null cells replaced with a blank value, refer to this SQL Fiddle.

SQL Self Join with null values

I am having all employees(manager and employees) under one table called Employee. Table looks as follows,
Table
+-------+------------+---------+---------+------------+
|emp_id | name | dept_id | salary | manager_id |
+=======+============+=========+=========+============+
| 1 | Sally | 1 | 20000 | null |
| 2 | Ajit | 2 | 20000 | 1 |
| 3 | Rahul | 1 | 20000 | 1 |
| 4 | uday | 1 | 20000 | null |
| 5 | john | 1 | 20000 | null |
| 6 | netaji | 2 | 20000 | 2 |
| 7 | prakriti | 3 | 1111 | 3 |
| 8 | sachin | 3 | 1111 | 3 |
| 9 | santosh | 1 | 1111 | 2 |
| 10 | Ravi | 1 | 1111 | 2 |
+-------+------------+---------+---------+------------+
Both managers and employees belong to same table. manager_id refers = emp_id who is manager.
I want to write query to count number of employees belonging to each manager. So even if certain manager doesn't have any employee under her or him the count will show as 0
Result should be as follows,
Expected Output
+------+----------+
|Count | Manager |
+======+==========+
| 2 | Sally |
| 3 | Ajit |
| 2 | Rahul |
| 0 | Uday |
| 0 | John |
+------+----------+
You need to do left self-join on the table. The left join will ensure that there is a row for every manager even if there are no employees under them. You need to use the COUNT() aggregate on a field from the employee side of the join that will be NULL if the manager has no employees. COUNT() doesn't actually count NULLs so this should give you zeroes where you want them.
The WHERE clause in this query defines managers by looking if their manager_id is NULL or if there are any matches in the joined table which means there are people that have them set as their manager.
SELECT mgr.name, COUNT(emp.emp_id) AS employee_count
FROM Employee AS mgr
LEFT JOIN Employee AS emp ON emp.manager_id=mgr.emp_id
WHERE mgr.manager_id IS NULL OR emp.emp_id IS NOT NULL
GROUP BY mgr.name
The correct solution likely involves fixing the scheme as any approach will fail for a "sub-manager" (who is managed and thus has a manager_id) but does not currently manage anybody.
Anyway, if the above limitation is acceptable, then people are managers if either
They have a NULL manager_id (as stated in a comment), or
They currently manage people other employees
Then this query (example sqlfiddle) can be used:
SELECT m.name as Manager, COUNT(e.id) as `Count`
FROM employee m
LEFT JOIN employee e
ON m.id = e.manager_id
GROUP BY m.id, m.name, m.manager_id
HAVING `Count` > 0 OR m.manager_id IS NULL
Notes/explanation:
The LEFT [OUTER] join is important here; otherwise managers who did not manage anybody would not be found. The filtering is then applied via the HAVING clause on the grouped result.
The COUNT is applied to a particular column, instead of *; when done so, NULL values in that column are not counted. In this case that means that employees (m) without a match (e) are not automatically selected by the COUNT condition in the HAVING. (The LEFT JOIN leaves in the left-side records, even when there is no join-match - all the right-side columns are NULL in this case.)
The GROUP BY contains all the grouping fields, even if they appear redundant. This allows the manager_id field to be used in the HAVING, for instance. (The group on ID was done in case two managers ever have the same name, or it is to be selected in the output clause.)
here is the solution; you are to make self join on employee table.
SELECT e1.manager_id, e2.name, COUNT (1) AS COUNT
FROM Employee e1 JOIN Employee e2 ON e1.manager_id = e2.id
GROUP BY e1.manager_id, e2.name
UNION ALL
SELECT e3.id, e3.name, 0 AS COUNT
FROM Employee e3
WHERE manager_id IS NULL
AND e3.id NOT IN ( SELECT e1.manager_id
FROM Employee e1
JOIN
Employee e2
ON e1.manager_id = e2.id
GROUP BY e1.manager_id, e2.name)
Maybe that helps:
select t1.name, count(*) -- all managers with emps
from t t1
join t t2
on t1.emp_id = t2.manager_id
group
by t1.name
union
all
select t1.name, 0 -- all managers without emps
from t t1
left
join t t2
on t1.emp_id = t2.manager_id
where t1.manager_id is null
and t2.emp_id is null
try below:
select (select count(*) from employees b where b.manager_id = a.emp_id)) as Count, a.Name as manager from employees a where a.emp_id in (select distict c.manager_id from employees c)
Query
CREATE TABLE employee(emp_id varchar(5) NOT NULL,
emp_name varchar(20) NULL,
dt_of_join date NULL,
emp_supv varchar(5) NULL,
CONSTRAINT emp_id PRIMARY KEY(emp_id) ,
CONSTRAINT emp_supv FOREIGN KEY(emp_supv)
REFERENCESemployee(emp_id));
you need to do
LEFT OUTER JOIN like this:
SELECT movies.title,sequelies.title AS sequel_title
FROM movies
LEFT OUTER JOIN movies sequelies
ON movies.sequel_id = sequelies.id ;