Combine information from 3 tables in one table (MySQL) - mysql

I have three tables like this
couples
+----------+-------------+---------------+-----------------+
|couple_id | clubname | agegroup | group |
+----------+-------------+---------------+-----------------+
| 36 | club_1 | adults | C |
| 37 | club_2 | youth | A |
+----------+-------------+---------------+-----------------+
users:
+----------+-------------+---------------+
|couple_id | firstname | lastname |
+----------+-------------+---------------+
| 36 | andy | hort |
| 36 | katrin | wilson |
| 37 | hans | gertes |
| 37 | julia | menz |
+----------+-------------+---------------+
locations for training:
+----------+-------------+
|couple_id | location |
+----------+-------------+
| 36 | Paris |
| 37 | Berlin |
| 37 | Paris |
+----------+-------------+
The resulting table should look like this:
+---------+------------------------+--------+-----+----------------+
|couple_id| couple |agegroup|group|location |
+---------+------------------------+--------+-----+----------------+
| 36 |andy hort, katrin wilson| adults | C | Paris |
| 37 |hans gertes, julia menz | youth | A | Paris, Berlin |
+---------+------------------------+--------+-----+----------------+
Is there an elegant query to join these information in one query?

You should use group_concat with distinct:
select c.couple_id,
group_concat(distinct concat(u.firstname, " ", u.lastname)) couple,
c.agegroup,
c.groupd,
group_concat(distinct l.location) location
from couple c
join users u on c.couple_id = u.couple_id
join locations l on c.couple_id = l.couple_id
group by c.couple_id
If you have records in the couple table that may not exist in the users or locations table, then you may need to use an outer join instead.
#spencer7593 makes a great point -- you can move the aggregation to subqueries to include potential duplicates being removed by distinct:
select c.couple_id,
u.couple,
c.agegroup,
c.groupd,
l.location
from couple c
join (
select couple_id,
group_concat(concat(firstname, " ", lastname)) couple
from users
group by couple_id
) u on c.couple_id = u.couple_id
join (
select couple_id,
group_concat(location) location
from locations
group by couple_id
) l on c.couple_id = l.couple_id

Related

Combining three SQL queries into one

I got working code from three queries but I would like to combine them into one or two. Basically I am checking if a provided phone number exists in table contacts or leads as well as if it exists as a secondary number in customfieldsvalues (not all leads have a customfield value though). I am using a CRM system based on CodeIgniter.
What I want to do (non-correct/hypothetical query):
SELECT * FROM contacts OR leads WHERE phonenumber = replace(X, '-', '')
OR leads.id = customvaluefields.relid AND cfields.fieldid = 41 AND cfields.value = X
Tables
table : contacts
+-------+----------------+----------------+
| id | firstname | phonenumber |
+-------+----------------+----------------+
| 1 | John | 214-444-1234 |
| 2 | Mary | 555-111-1234 |
+-------+----------------+----------------+
table : leads
+-------+-----------+---------------------+
| id | name | phonenumber |
+-------+-----------+---------------------+
| 1 | John | 214-444-1234 |
| 2 | Mary | 555-111-1234 |
+-------+-----------+---------------------+
table : customvaluefields
+-------+-----------+-------------+-----------+
| id | relid | fieldid | value |
+-------+-----------+-------------+-----------+
| 1 | 1 | 41 | 222333444 |
| 2 | 1 | 20 | Management|
| 3 | 2 | 41 | 333444555 |
+-------+-----------+-------------+-----------+
If I understand what you are trying to, maybe UNION ALL would work. This is something to get you started:
SELECT C.ID, C.FirstName, C.Phonenumber
FROM Contacts C
JOIN CustomValueField CVF
ON c.ID = CVF.RelID AND
CVF.ID = 41
AND REPLACE(Phonenumber,'-','') = cvf.Value
UNION ALL
SELECT L.ID, L.FirstName, L.Phonenumber
FROM Leads L
JOIN CustomValueField CVF
ON L.ID = CVF.RelID AND
CVF.ID = 41
AND REPLACE(Phonenumber,'-','') = cvf.Value
I'm joining the contacts and leads tables to CustomeValueField in each query and then UNION them together along with the WHERE clause in each. I'm sure it's not 100% correct for what you need, but should get you headed to a solution. Here is more information: https://dev.mysql.com/doc/refman/8.0/en/union.html

SQL create table with interconnected IDs between tables

I have two tables in a database that I would like to combine in a specific way.
Here are the tables:
table: watchhistory
customerid | titleid | rating | date
------------+-----------+--------+------------
1488844 | tt0389605 | 3 | 2005-09-06
1181550 | tt0389605 | 3 | 2004-02-01
1227322 | tt0389605 | 4 | 2004-02-06
786312 | tt0389605 | 3 | 2004-11-16
525356 | tt0389605 | 2 | 2004-07-11
1009622 | tt0389605 | 1 | 2005-01-19
table: media
mediaid | directorid | title | genre | runtime | releasedate
-----------+------------+----------------+----------------------+---------+-------------
tt0090557 | nm0851724 | Round Midnight | [Drama, Music] | 133 | 1986
tt0312296 | nm0146385 | 1 Giant Leap | [Documentary, Music] | 155 | 2002
tt0078721 | nm0001175 | 10 | [Comedy, Romance] | 122 | 1979
tt2170245 | nm3593080 | 10 | [Thriller] | 76 | 2012
tt5282238 | nm6207118 | 10 | [Thriller] | 90 | 2015
tt0312297 | nm0302572 | 10 Attitudes | [Comedy, Drama] | 87 | 2001
I would like to make a table with the following columns:
title (from media) | Views#
I created this query to get the top 10 titleids, meaning the top 10 titles from watchhistory that appear in watchhistory the most times:
SELECT titleid, count(*) as Views FROM watchhistory GROUP BY titleid ORDER BY Views DESC limit 10;
titleid | views
------------+-------
tt7631348 | 1307
tt14627576 | 1065
tt8372506 | 1063
tt5793632 | 1056
tt1403008 | 1053
tt7825602 | 1051
tt6840954 | 1046
tt12780424 | 1042
tt7266106 | 1036
tt6539274 | 1035
The goal is to essentially replace this titleid column (from watchhistory) with the title (from media). I tried using joins between the watchhistory.titleid and media.mediaid with no luck.
What SQL query do I need to get this desired table?
Thanks in advance.
You need to INNER JOIN to your media table on mediaid:
SELECT m.title, count(wh.*) as Views
FROM watchhistory wh
INNER JOIN media m on m.mediaid = wh.titleid
GROUP BY m.mediaid
ORDER BY Views DESC LIMIT 10;
To see what the select and join are doing, you can simplify it:
SELECT m.*, wh.*
FROM watchhistory wh
INNER JOIN media m on m.mediaid = wh.titleid
The result will be a joined 'table' that has the two tables combined on the mediaid/titleid.

MYSQL selecting new column based on multiple joins

I'm still working through some kinks with MySQL so any help will be appreciated.
I have 3 tables -- equipment, states, zones.
equipment:
+---------------+------+------------+
| current_state | id | ...columns |
+---------------+------+------------+
states:
+----------+-------------+
| state | zone_id |
+----------+-------------+
zones:
+-----+------+
| id | zone |
+-----+------+
In equipment, there is one current_state per row.
In states, there is one zone_id per row.
In zones, there is one zone per row.
I would like to JOIN the three tables as a subquery select statement (not even sure if that's a thing) and have the output return as 1 alias'd column among the other columns I'm selecting
+--------------+-------------+
| current_zone | ....columns |
+--------------+-------------+
A sample expected output is:
+------------+-------------+--------+------------------+--------------+---------+
| c_id | g_id | e_id | equipment_type | impressionId | email |
+------------+-------------+--------+------------------+--------------+---------+
| 1234 | ABC1234 | 0001 | VEST | 2032 |ab#yc.com|
| 1234 | 1234ABC | 0001 | SHIRT | 4372 |ab#yc.com|
| 1234 | DCBA123 | 0001 | CAN | 4372 |ab#yc.com|
| 1234 | DCBA321 | 0001 | JACKET | ab#yc.com |ab#yw.com|
| 4567 | abc321d | 0002 | SHIRT | 2032 |db#yw.com|
| 4567 | cba123d | 0002 | CAN | 4372 |db#yw.com|
| 4567 | def4rg4 | 0002 | JEANS | 3210 |db#yw.com|
+------------+-------------+--------+------------------+--------------+---------+
The current query has multiple joins already referring to the zones and states table in order to determine a different value:
SELECT equipment.*,
...
FROM equipment
LEFT JOIN c on equipment.c_id = c.id
LEFT JOIN g on equipment.g_id = g.id
LEFT JOIN states on g.state = states.state
LEFT JOIN zones on zones.id = states.zone_id
Essentially, what I want to do is create a subquery in order to create a new column based on the results of the three joins, something like this:
SELECT equipment.*,
(SELECT
equipment.current_state
FROM equipment
LEFT JOIN equipment.current_state = states.state
LEFT JOIN zones.id = states.zone_id
) as current_zone,
...
This is even possible? Am I trying to select a new column in the wrong place?
Thanks to #TheImpaler I was able to clear up my Scalar Subquery. In my eyes, I thought I had to create another join based on the properties I wanted when in reality all I had to do was create a conditional scalar subquery:
SELECT equipment.*,
(SELECT zones.zone
FROM zones
WHERE equipment.current_state = states.state
AND zones.id = states.zone_id
) as current_zone,
...

Multiple rows with same identifier, how to select the row with heighest value (ignoring the rest) with multiple left joins?

this issue has been bothering me for a few hours now. After finding out my old query had an issue, I had to rebuild it.
The situation:
I need to match each patient_id with a clinic_id, and for that I get all the appointments using the patient_id,
find the highest appointment_id and use its clinic_id to set the last known clinic_id.
My old query did this, but it skipped patients that never had an appointment.
These are my current results, but I need to filter my results. Question is, how?
+---------------+-------------------+-------------------+---------------+
| patient_id | country_code | appointment_id | clinic_id |
+---------------+-------------------+-------------------+---------------+
| 111 | UK | 620 | 3 |
| 111 | UK | 621 | 2 |
| 111 | UK | 1995 | 1 |
| 222 | UK | 609 | 3 |
| 222 | UK | 610 | 2 |
| 333 | UK | null | null |
| 444 | UK | null | null |
+---------------+-------------------+-------------------+---------------+
What I want is the following:
+---------------+-------------------+-------------------+---------------+
| patient_id | country_code | appointment_id | clinic_id |
+---------------+-------------------+-------------------+---------------+
| 111 | UK | 1995 | 1 |
| 222 | UK | 610 | 2 |
| 333 | UK | null | null |
| 444 | UK | null | null |
+---------------+-------------------+-------------------+---------------+
I am using the following query right now:
SELECT
patient.id,
systemcountry.country_code,
appointment_patient.appointment_id,
appointment.clinic_id
FROM
patient
LEFT JOIN
systemcountry ON patient.country_id = systemcountry.id
LEFT JOIN
appointment_patient ON patient_id = patient.id
LEFT JOIN
appointment ON appointment_patient.appointment_id = appointment.id
This was my old query, which had an issue causing it to skip patients that never had an appointment:
SELECT
patient.id AS patient_id,
systemcountry.code AS systemcountry_code,
appointment.clinic_id
FROM
patient
LEFT JOIN
systemcountry ON patient.land_id = systemcountry.id,
appointment
WHERE
appointment.id = (SELECT
MAX(appointment_id)
FROM
appointment_patient
WHERE
patient_id = patient.id);
I am still a beginner, so go easy on me.
I appreciate any input, thanks!
Move the sub-select in your original query's WHERE clause into a LEFT JOIN (something like this):
LEFT JOIN
(SELECT MAX(appointment_id), patient_id
FROM appointment_patient
GROUP BY patient_id) as apt ON patient.patient_id=apt.patient_id
You can try function max() over columns whose only highest value you want, then group by the result set using patient.id
SELECT
patient.id,
systemcountry.country_code,
max(appointment_patient.appointment_id),
appointment.clinic_id
FROM
patient
LEFT JOIN
systemcountry ON patient.country_id = systemcountry.id
LEFT JOIN
appointment_patient ON patient_id = patient.id
LEFT JOIN
appointment ON appointment_patient.appointment_id = appointment.id
GROUP BY patient.id

grouping relationships of many-to-many-to many

I have a requirement to map a tax rate to a person based on the country the person was resident at that time.
tbl: person
| p_id | name_first | name_last |
=======++========================
| 1 | john | smith |
| 2 | joanne | smyth |
tbl: person_in_country
| p_id | iso | arrived |
===========================
| 1 | GB | 1980-01-01 |
| 2 | FR | 1987-03-21 |
| 1 | FR | 2003-06-17 |
| 1 | JP | 2008-07-02 |
| 2 | GB | 2008-10-01 |
| 1 | GB | 2009-01-10 |
tbl: country
| iso | ctry_name |
========================
| GB | United Kingdom |
| FR | France |
| JP | Japan |
tbl: tax_rates
| iso | tax_rate | tax_date |
===============================
| GB | 17.5 | 1970-01-01 |
| FR | 15.0 | 1977-03-21 |
| JP | 12.0 | 1977-06-17 |
| FR | 15.0 | 1994-03-21 |
| JP | 18.5 | 2008-07-02 |
| GB | 15 | 2008-04-01 |
| GB | 20 | 2010-05-01 |
So I need tuples containing the person in the country and the tax rate that they should have at a given time..
Something along the lines of:
select p.p_id, p.name_first, p.name_last,
pic.arrived,
c.iso, c.ctry_name,
t.tax_rate
from people p
left join (select * from person_in_country order by arrived desc) pic using (p_id)
left join country c on c.iso = pic.iso
left join (select * from tax_rates order by tax_date desc) t on t.iso = c.iso
where t.tax_date <= NOW()
group by p.pid, pic.arrived, t.tax_date
Hope this make sense... and many thanks in advance
Actually, you have to do a query basicly in three steps. First one you are going to retrieve a kind of "raw data" with all desired columns, joining the relating tables, whatever these columns will be used to join or to retrieve choosen data.
After that, you have to group data in order to turn only last dates from the matching join.
Finally, you have to query again tax table to retrieve tax in the tax date current at the moment of arrival.
It is possible that there is a more easy or ellegant way to do so, but this query is working. Check your system performance depending on the query demmands. It seems a bit hard at a first glance, but it isn't when taking a more carefull look. The SQL code:
SELECT
c02.iso,
c02.p_id,
c02.name_first,
c02.name_last,
c02.ctry_name,
c02.arrived,
c02.mtax_date,
tax_rates.tax_rate
FROM (
SELECT
c01.iso,
c01.p_id,
c01.name_first,
c01.name_last,
c01.ctry_name,
c01.arrived,
Max(c01.tax_date) AS mtax_date
FROM (
SELECT
country.iso,
person.p_id,
person.name_first,
person.name_last,
country.ctry_name,
person_in_country.arrived,
tax_rates.tax_date
FROM
tax_rates
INNER JOIN (
country
INNER JOIN (
person
INNER JOIN
person_in_country
ON
person.p_id = person_in_country.p_id
)
ON
country.iso = person_in_country.iso
)
ON
tax_rates.iso = person_in_country.iso
GROUP BY
country.iso,
person.p_id,
person.name_first,
person.name_last,
country.ctry_name,
person_in_country.arrived,
tax_rates.tax_date
HAVING (((tax_rates.tax_date)<=[arrived]))
) as c01
GROUP BY
c01.iso,
c01.p_id,
c01.name_first,
c01.name_last,
c01.ctry_name,
c01.arrived
) as c02
INNER JOIN
tax_rates ON (
c02.mtax_date = tax_rates.tax_date
)
AND
(
c02.iso = tax_rates.iso
);
Output:
iso p_id name_first name_last ctry_name arrived mtax_date tax_rate
GB 1 john smith United Kindom 01/01/1980 01/01/1970 18
FR 2 joanne smyth France 21/03/1987 21/03/1977 15
FR 1 john smith France 17/06/2003 21/03/1994 15
JP 1 john smith Japan 02/07/2008 02/07/2008 18
GB 1 john smith United Kindom 10/01/2009 01/04/2008 15
GB 2 joanne smyth United Kindom 01/10/2008 01/04/2008 15