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

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

Related

MySQL GROUP_CONCAT with SUM() and multiple JOINs inside subquery

I'm very average with MySQL, but usually I can write all the needed queries after reading documentation and searching for examples. Now, I'm in the situation where I spent 3 days re-searching and re-writing queries, but I can't get it to work the exact way I need. Here's the deal:
1st table (mpt_companies) contains companies:
| company_id | company_title |
------------------------------
| 1 | Company A |
| 2 | Company B |
2nd table (mpt_payment_methods) contains payment methods:
| payment_method_id | payment_method_title |
--------------------------------------------
| 1 | Cash |
| 2 | PayPal |
| 3 | Wire |
3rd table (mpt_payments) contains payments for each company:
| payment_id | company_id | payment_method_id | payment_amount |
----------------------------------------------------------------
| 1 | 1 | 1 | 10.00 |
| 2 | 2 | 3 | 15.00 |
| 3 | 1 | 1 | 20.00 |
| 4 | 1 | 2 | 10.00 |
I need to list each company along with many stats. One of stats is the sum of payments in each payment method. In other words, the result should be:
| company_id | company_title | payment_data |
--------------------------------------------------------
| 1 | Company A | Cash:30.00,PayPal:10.00 |
| 2 | Company B | Wire:15.00 |
Obviously, I need to:
Select all the companies;
Join payments for each company;
Join payment methods for each payment;
Calculate sum of payments in each method;
GROUP_CONCAT payment methods and sums;
Unfortunately, SUM() doesn't work with GROUP_CONCAT. Some solutions I found on this site suggest using CONCAT, but that doesn't produce the list I need. Other solutions suggest using CAST(), but maybe I do something wrong because it doesn't work too. This is the closest query I wrote, which returns each company, and unique list of payment methods used by each company, but doesn't return the sum of payments:
SELECT *,
(some other sub-queries I need...),
(SELECT GROUP_CONCAT(DISTINCT(mpt_payment_methods.payment_method_title))
FROM mpt_payments
JOIN mpt_payment_methods
ON mpt_payments.payment_method_id=mpt_payment_methods.payment_method_id
WHERE mpt_payments.company_id=mpt_companies.company_id
ORDER BY mpt_payment_methods.payment_method_title) AS payment_data
FROM mpt_companies
Then I tried:
SELECT *,
(some other sub-queries I need...),
(SELECT GROUP_CONCAT(DISTINCT(mpt_payment_methods.payment_method_title), ':', CAST(SUM(mpt_payments.payment_amount) AS CHAR))
FROM mpt_payments
JOIN mpt_payment_methods
ON mpt_payments.payment_method_id=mpt_payment_methods.payment_method_id
WHERE mpt_payments.company_id=mpt_companies.company_id
ORDER BY mpt_payment_methods.payment_method_title) AS payment_data
FROM mpt_companies
...and many other variations, but all of them either returned query errors, either didn't return/format data I need.
The closest answer I could find was MySQL one to many relationship: GROUP_CONCAT or JOIN or both? but after spending 2 hours re-writing the provided query to work with my data, I couldn't do it.
Could anyone give me a suggestion, please?
You can do that by aggregating twice. First for the sum of payments per method and company and then to concatenate the sums for each company.
SELECT x.company_id,
x.company_title,
group_concat(payment_amount_and_method) payment_data
FROM (SELECT c.company_id,
c.company_title,
concat(pm.payment_method_title, ':', sum(p.payment_amount)) payment_amount_and_method
FROM mpt_companies c
INNER JOIN mpt_payments p
ON p.company_id = c.company_id
INNER JOIN mpt_payment_methods pm
ON pm.payment_method_id = p.payment_method_id
GROUP BY c.company_id,
c.company_title,
pm.payment_method_id,
pm.payment_method_title) x
GROUP BY x.company_id,
x.company_title;
db<>fiddle
Here you go
SELECT company_id,
company_title,
GROUP_CONCAT(
CONCAT(payment_method_title, ':', payment_amount)
) AS payment_data
FROM (
SELECT c.company_id, c.company_title, pm.payment_method_id, pm.payment_method_title, SUM(p.payment_amount) AS payment_amount
FROM mpt_payments p
JOIN mpt_companies c ON p.company_id = c.company_id
JOIN mpt_payment_methods pm ON pm.payment_method_id = p.payment_method_id
GROUP BY p.company_id, p.payment_method_id
) distinct_company_payments
GROUP BY distinct_company_payments.company_id
;

MySQL - Retrieve results even if join is null

I have the following sql working with 1 minor issue that I need help with. I am needing the results to show even if one of the joined tables is null. I have tried using a left/right join, but it didn't seem to make a difference, what do I need to use to make this work?
As it is now, the query will work unless the record in the table dx_code_patient has no match, I need it show the results even if there are no matching records in that table.
SELECT
group_concat(distinct current_dx.dx_code_with, ': ', current_dx.description SEPARATOR ' - ') AS current_dxc,
group_concat(distinct pending_dx.dx_code_with, ': ', pending_dx.description SEPARATOR ' - ') AS pending_dxc,
p.id, p.first_name, p.last_name
FROM patients AS p
INNER JOIN tmp_dx_code_patient AS tmp_dx
ON tmp_dx.patient_id = p.id
INNER JOIN dx_code_patient AS cdx
ON cdx.patient_id = p.id
INNER JOIN dx_codes AS current_dx
ON current_dx.id = cdx.dx_code_id
INNER JOIN dx_codes AS pending_dx
ON pending_dx.id = tmp_dx.dx_code_id
GROUP BY p.id
ORDER BY tmp_dx.created_at asc
current results:
+----------------+--------------+----+------------+-----------+
| current_dxc | pending_dxc | id | first_name | last_name |
+----------------+--------------+----+------------+-----------+
| def: something | 123: message | 2 | Bob | Smith |
+----------------+--------------+----+------------+-----------+
Expected
+----------------+---------------+----+------------+------------+
| current_dxc | pending_dxc | id | first_name | last_name |
+----------------+---------------+----+------------+------------+
| null | ghy: hi | 1 | Mike | Jones |
+----------------+---------------+----+------------+------------+
| def: something | 123: message | 2 | Bob | Smith |
+----------------+---------------+----+------------+------------+
| null | 432: question | 3 | John | Doe |
+----------------+---------------+----+------------+------------+
JOIN does not return NULL results. My guess is that you mean no rows match. If so, a LEFT JOIN should solve your problem. Use it throughout the FROM clause:
FROM patients p LEFT JOIN
tmp_dx_code_patient tmp_dx
ON tmp_dx.patient_id = p.id LEFT JOIN
dx_code_patient cdx
ON cdx.patient_id = p.id LEFT JOIN
dx_codes current_dx
ON current_dx.id = cdx.dx_code_id LEFT JOIN
dx_codes pending_dx
ON pending_dx.id = tmp_dx.dx_code_id

SQL n:m relation

I have the following tables (unrelated columns left out):
studios:
id | user_id
1 | 1
2 | 1
equipment:
id
1
2
studio_equipment:
id | studio_id | equipment_id
1 | 1 | 1
2 | 1 | 1
I have "studios" and "equipment". A studio belongs to a user. Equipment can be assigned to studios (studio_equipment table). An equipment can be assigned multiple times to a studio but there can also be studios that have no equipment yet.
I want to retrieve all studios for a certain user together with all the possible equipment that could be assigned to these studios. If an equipment has already been assigned to a studio, then show this aswell.
For the example above that would mean the following (for user_id 1):
desired results:
studio.id | equipment.id | studio_equipment.id
1 | 1 | 1
1 | 1 | 2
1 | 2 | null
2 | 1 | null
2 | 2 | null
This is my SQL statement thus far:
SELECT `s`.*, `e`.*, `se`.*
FROM (`studios` AS s)
LEFT JOIN `studio_equipment` AS se ON `s`.`id`=`se`.`studio_id`
LEFT OUTER JOIN `equipment` AS e ON `se`.`equipment_id`=`e`.`id`
WHERE `s`.`user_id` = '1'
But this does not retrieve all the data i want. For Example studio 2 is retrieved but not paired with all the possible equipment.
Thanks in advance for your help!
SELECT
s.id AS studio_id,
e.id AS equipment_id,
se.id AS studio_equipment_id
FROM
studios AS s
CROSS JOIN
equipment AS e
LEFT JOIN
studio_equipment AS se
ON se.studio_id = s.id
AND se.equipment_id = e.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

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

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.