Join 2 tables, print data from 2nd table when joined rows occur - mysql

The case:
I have 2 tables, 'contracts' and 'members' tied with contract.id = members.cid.
Each contract has one main member and several secondary members (usually the children and spouse of the main member). The main member's details (name, address etc) are stored in table contracts whereas, extra members details are kept in table members. (bad logic, i know but this was a mess to begin with and i need time to redesign the db and website)
The desired output:
When I run a batch print of all contracts (lets say, every Friday) I need to also print a copy of the contract for each member, too but with the member's details on the contract instead of the main member.
The question:
How does this translate into a mysql query? Ok, its a left join, but how do I say "print data from table members instead of contracts for the joined rows"?
Main fields that occur in the 2 tables are name + surname, those should be enough for a draft query example.
Example tables and data:
contracts
-------------------------
id | name | surname |
-------------------------
1 | Tom | Jones |
2 | Jamie | Oliver |
members
--------------------------------
id | cid | name | surname |
--------------------------------
1 | 1 | Jack | Jones |
2 | 1 | Anne | Jones |
3 | 2 | Cathy | Wilson |
So the results I want shoudld be:
cid | name | surname |
--------------------------
1 | Tom | Jones |
1 | Jack | Jones |
1 | Anne | Jones |
2 | Jamie | Oliver |
2 | Cathy | Wilson |
If i write
SELECT c.name as name, c.surname as surname, m.name as name, m.surname as surname
FROM contracts c
join members m on c.id = m.cid
I simply end up with
name and name_1, surname and surname_1 but I want ALL names to fall under name and likewise for all other matching columns.

Hope this works :::
select c1.id, c1.name, c1.surname
from contracts c1
union
(Select m.id, m.name, m.surname
from
members m left join contracts c on (c.id = m.cid))

This is what I finally did and it worked (field names are different but the syntax is what matters):
SELECT u.id, u.onoma_u, u.name_u,
coalesce(u.programa, aa.programa) as programa,
coalesce(u.barcode, aa.barcode) as barcode,
coalesce(u.plan, aa.plan) as plan,
coalesce(u.im_exp, aa.im_exp) as im_exp,
coalesce(u.symb, aa.symb) as symb
FROM (SELECT a1.id, a1.onoma_u, a1.name_u, a1.programa, a1.barcode, a1.plan, a1.im_exp, a1.symb
FROM aitisi a1
UNION
SELECT a2.id, m.name, m.surname, NULL, NULL, NULL, NULL, NULL
FROM members m
JOIN aitisi a2 ON a2.id = m.symbid) u
JOIN aitisi aa ON aa.id = u.id;
I used aliases and NULLS as dummy fields to fill in the blanks.

Related

How to SELECT data which equals either Type X or Type Y in a MySQL join?

I'm trying to complete a MySQL task which has the following 2 tables.
INSERT INTO STUDENT VALUES
(100,'Oliver','Jake','Fearon Rd','Morrisville',13408,'NY'),
(101,'Jack','Connor','Road 124','Visalia',93291,'CA'),
(102,'Harry','Callum','Worth Ave','California',20619,''),
(103,'Bella','Swan','Butler Rd','Morrisville',13408,'NY'),
(104,'Charlie','Kyle','Womack Rd','Sanford',27330,'NC'),
(105,'Jinnie','Reece','Boyer Ave','Hammonton',08037,'NJ'),
(107,'Jessica','Davies','Worth Ave','California',20619,'CA'),
(108,'Isabella','Brown','Hawkins Ave','Sanford',27330,'NC');
INSERT INTO ENROLLS VALUES
(100,'CSC11',1),
(100,'CSC11',2),
(101,'CSC11',1),
(101,'CSC12',1),
(101,'CSC13',1),
(102,'CSC12',1),
(103,'CSC12',1),
(104,'CSC13',1),
(105,'CSC12',1),
(105,'CSC13',1),
(105,'CSC14',1),
(106,'CSC14',1),
(107,'CSC14',1),
(108,'CSC14',1);
Retrieve information (SSN, first name, and last name) about students who are enrolled in the course of ‘CSC11’ or ‘CSC12’ (‘CSC11’ and ‘CSC12’ are course numbers). Note: MySQL does not support “INTERSECT”.
According to the question Students, 100,102,103 and 105 should have come as the answers. I've tried following gives the 101 also which includes both CSC11 and CSC12.
mysql> SELECT s.SSN, s.First_Name, s.Last_Name
-> FROM STUDENT s
-> JOIN ENROLLS e ON s.SSN= e.SSN
-> WHERE (e.CourseNo = 'CSC11') OR (e.CourseNo = 'CSC12')
-> GROUP BY e.SSN;
+-----+------------+-----------+
| SSN | First_Name | Last_Name |
+-----+------------+-----------+
| 100 | Oliver | Jake |
| 101 | Jack | Connor |
| 102 | Harry | Callum |
| 103 | Bella | Swan |
| 105 | Jinnie | Reece |
+-----+------------+-----------+
So kindly explain to me the mistake I have made here and the correct query.
Add a HAVING clause which asserts that one, but not both, course numbers be present:
SELECT s.SSN, s.First_Name, s.Last_Name
FROM STUDENT s
INNER JOIN ENROLLS e ON s.SSN = e.SSN
WHERE e.CourseNo IN ('CSC11', 'CSC12')
GROUP BY e.SSN
HAVING MIN(e.CourseNo) = MAX(e.CourseNo);
Demo
Note: The above assumes that SSN is a primary key in the student table.

My query is missing a row that has a zero

I searched for an answer to this but didn't find one for this specific problem. I am making a database for storing classes created by a teacher, and a lookup table for students enrolled in each class. I want to run a query that shows each teacher's class and the number of students. As you can see in the data below, the teacher has two classes with zero students, but only one of them is returning in the query.
The query:
SELECT `c`.`ClassID` AS `id`,
`c`.`Class_Name` AS `name`,
`c`.`Class_Code` AS `code`,
COUNT(`e`.`EnrollID`) AS `count`
FROM `CMP_Classes` `c`
LEFT OUTER JOIN `CMP_Student_Enrollment` `e`
ON `c`.`ClassID` = `e`.`Enroll_ClassID`
AND `Class_Teacher` = 1
GROUP BY `e`.`EnrollID`
The data:
CMP_Classes Table:
ClassID | Class_Name | Class_Code | Class_Teacher
1 | Mr. Jones' 1st Period Class | QYTNPCGK | 1
2 | Mr. Jones' 2nd Period Class | HZWNDZPM | 1
3 | Pizza | RRCXQNNE | 9
4 | Mr. Jones' 3rd Period Class | NFLBXFEQ | 1
CMP_Student_Enrollment Table:
EnrollID | Enroll_Student | Enroll_ClassID
3 | 2 | 1
Query results:
id | name | code | count
2 | Mr. Jones' 2nd Period Class | HZWNDZPM | 0
1 | Mr. Jones' 1st Period Class | QYTNPCGK | 1
So, as you can see, there is no row for "Mr. Jones' 3rd Period Class" but there should be.
You need to group by the unaggregated columns in the select:
SELECT c.ClassID AS id, c.Class_Name AS name, c.Class_Code AS code,
COUNT(e.EnrollID) AS `count`
FROM CMP_Classes c LEFT OUTER JOIN
CMP_Student_Enrollment e
ON c.ClassID = e.Enroll_ClassID
WHERE c.Class_Teacher = 1
GROUP BY c.ClassID, c.Class_Name, c.Class_Code;
All the backticks in your query make it harder to read and write.

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 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 ;

Using subqueries with multiple results which depend on the main query

I have a persons table called "tc_person" and a marriage table called "tc_marriage". I want to select a few columns from my persons table and one column which represents the id of the partner.
The marriage table includes the pid_1 and pid_2 of two people - but it is important that there is only one entry for a couple and the order of the couples ids may vary. Here's the tables:
tc_person:
| id | name | lastname |
--------------------------------------
| 4 | peter | smith |
| 5 | sarah | smith |
tc_marriage:
| id | pid_1 | pid_2 |
--------------------------------------
| 0 | 5 | 4 |
| 1 | 7 | 9 |
It seems that my subquery is interpreted as a whole before the original select statement. Now I get the error that my subquery returns more than one row.
SELECT p.id, p.name, p.lastname,
(SELECT m.pid_1 FROM tc_marriage m WHERE m.pid_2 = p.id UNION
SELECT m.pid_2 FROM tc_marriage m WHERE m.pid_1 = p.id) as partner_id
FROM tc_person p WHERE p.lastname LIKE 'smith';
I am looking for the following output:
| id | name | lastname | partner_id |
-----------------------------------------------------
| 4 | peter | smith | 5 |
| 5 | sarah | smith | 4 |
Is this even possible with only one single query? You can probably tell by now that I'm quite the SQL noob. Maybe you guys can help.
You can use IN to avoid a UNION (which is typically slower), and a CASE statement to pick out the correct partner_id:
SELECT p.id,
p.name,
p.last_name,
CASE p.id WHEN m.pid_1 THEN m.pid_2 ELSE m.pid_1 END AS partner_id
FROM tc_person p
JOIN tc_marriage m ON p.id IN (m.pid_1, m.pid_2)
What you want to do is to join the person table to the marriage table. However, the problem is that you have two keys. One solution is to do two joins and then some logic to choose the right value.
I prefer to "double" the marriage table by swapping the partners. The following query takes this approach in a subquery:
select p.id, p.name, p.lastname, partner_id
from tc_person p join
(select pid_1 as firstid, pid_2 as partner_id
from marriage
union all
select pid_2 as firstid, pid_1 as partner_id
from marriage
) m
on p.id = m.firstid