MySQL - Join and conditional select columns - mysql

Don't know how to excatly name the problem. Therefore my example tables:
People:
id_people | peopleName |
------------------------
1 | John |
2 | Jane |
3 | Frank |
4 | Emma |
Courses:
id | id_course | id_people | date |
-----------------------------------------
1 | 1 | 1 | 2011-02-14 |
2 | 2 | 1 | 2013-05-01 |
3 | 3 | 1 | 2015-08-25 |
4 | 1 | 2 | 2012-01-05 |
5 | 1 | 4 | 2017-10-10 |
Now I want to generate a table where I have one row per person and for every course a column with the date like:
id_people | peopleName | Course1 | Course2 | Course3 |
---------------------------------------------------------------
1 | John | 2011-02-14 | 2013-05-01 | 2015-08-25 |
2 | Jane | 2012-01-05 | | |
3 | Frank | | | |
4 | Emma | 2017-10-10 | | |
I tried following query:
SELECT *,
max(case when courses.id_course = 1 then cource.date end) as Course1,
max(case when courses.id_course = 2 then cource.date end) as Course2,
max(case when courses.id_course = 3 then cource.date end) as Course3
FROM people
JOIN courses ON people.id_people = courses.id_people
GROUP BY people.id_people
But I got the error #1055 ([..] this is incompatible with sql_mode=only_full_group_by).
So, it forces me to add the column course.id, course.id_course and course.date to the group by statemant. This results in a row per per people and course.
How can I get my desired resulting table with a sql statment?
I do not have the privilege to change the sql_mode variable.

So it forces me to add the column course.id, course.id_course and
course.date to the group by statemant...
No it does not.
You only need to change the join to a LEFT JOIN and group by people.id_people, people.peopleName and you will get the results that you want:
SELECT people.id_people, people.peopleName,
max(case when courses.id_course = 1 then courses.date end) as Course1,
max(case when courses.id_course = 2 then courses.date end) as Course2,
max(case when courses.id_course = 3 then courses.date end) as Course3
FROM people
LEFT JOIN courses ON people.id_people = courses.id_people
GROUP BY people.id_people, people.peopleName
See the demo.
Results:
| id_people | peopleName | Course1 | Course2 | Course3 |
| --------- | ---------- | ----------- | ----------- | ----------- |
| 1 | John | 2011-02-14 | 2013-05-01 | 2015-08-25 |
| 2 | Jane | 2012-01-05 | | |
| 3 | Frank | | | |
| 4 | Emma | 2017-10-10 | | |

You haven't define id_people to be a primary key or at least unique. You should probably do that.
In the meantime, you can aggregate first and then join:
SELECT *
FROM people p JOIN
(SELECT c.id_people
max(case when c.id_course = 1 then c.date end) as Course1,
max(case when c.id_course = 2 then c.date end) as Course2,
max(case when c.id_course = 3 then c.date end) as Course3
FROM courses c
GROUP BY c.id_people
) c
USING (id_people);

Related

Group data by foreign key and date with total by date

I need help to select daily payments made and group by the organization and date.
Group by date, then the total number of payments and the sum total amount of payments for each day
Tables are as follows,
organizations
-----------------------------
| id | name |
+-------------+-------------+
| 1 | org_1 |
+-------------+-------------+
| 2 | org_2 |
+-------------+-------------+
| 3 | org_2 |
-----------------------------
payments
------------------------------------------------------------
| id | org_id | amount | date_created |
+-----------+------------+-------------+-------------------+
| 1 | 2 | 20 | 2020-11-06 |
+-----------+------------+-------------+-------------------+
| 2 | 2 | 10 | 2020-11-06 |
+-----------+------------+-------------+-------------------+
| 3 | 1 | 50 | 2020-11-05 |
+-----------+------------+-------------+-------------------+
| 4 | 2 | 10 | 2020-11-05 |
------------------------------------------------------------
Expected Result
----------------------------------------------------------------------------------------------
| date_created | total_amount | num_payments | org_1 | org_2 | org_3 |
+----------------+----------------+-------------------+-----------+-------------+------------+
| 2020-11-06 | 30.00 | 2 | 0 | 2 | 0 |
+----------------+----------------+-------------------+-----------+-------------+------------+
| 2020-11-05 | 60.00 | 2 | 1 | 1 | 0 |
+----------------+----------------+-------------------+-----------+-------------+------------+
Use conditional aggregation:
select p.date_created,
sum(p.amount) as total_amount,
count(*) as num_payments,
sum(case when o.name = 'org_1' then p.amount else 0 end) as org_1,
sum(case when o.name = 'org_2' then p.amount else 0 end) as org_2,
sum(case when o.name = 'org_3' then p.amount else 0 end) as org_3
from payments p
inner join organizations o on o.id = p.org_id
group by p.date_created

Joining multiple rows with same ID in one

I am having trouble with an SQL query. I have two tables.
My first table:
+------------+-------------+---------------+
| id_mission | Some column | Other column |
+------------+-------------+---------------+
| 1 | ... | ... |
| 2 | ... | ... |
+------------+-------------+---------------+
My second table:
+------------+-------------+---------+
| id_mission | id_category | points |
+------------+-------------+---------+
| 1 | 1 | 3 |
| 1 | 2 | 4 |
| 1 | 3 | 4 |
| 1 | 4 | 8 |
| 2 | 1 | -4 |
| 2 | 2 | 3 |
| 2 | 3 | 1 |
| 2 | 4 | -7 |
+------------+-------------+---------+
And I would like to have this kind of result with my SELECT request
+------------+-------------+--------------+---------------+----------------+
| id_mission | Some column | Other column | id_category 1 | id_category X |
+------------+-------------+--------------+---------------+----------------+
| 1 | ... | ... | ... | ... |
| 2 | ... | ... | ... | ... |
+------------+-------------+--------------+---------------+----------------+
I have tried this with the first two column but it doesn't work, I also tried GROUP_CONCAT, it works but it's not the result I want.
SELECT m.id_mission ,mc.id_category 1,mc1.id_category 2
from mission m
left join mission_category mc on m.id_mission = mc.id_mission
left join mission_category mc1 on m.id_mission = mc1.id_mission
Can someone help me?
You can use conditional aggregation. Assuming that you want to pivot the points value per category:
select
t1.*,
max(case when t2.id_category = 1 then points end) category_1,
max(case when t2.id_category = 2 then points end) category_2,
max(case when t2.id_category = 3 then points end) category_3
from t1
inner join t2 on t2.id_mission = t1.id_mission
group by t1.id_mission
This assumes that id_mission is the primary key of t1 (else, you need to enumerate the columns you want in both the select and group by clauses).

Combining two mysql tables into one row

I'm trying to combine a few tables into a row.
Team Table:
+----+-------+
| id | team |
+----+-------+
| 10 | Team1 |
| 11 | Team2 |
| 12 | Team3 |
+----+-------+
Location Table:
+----+-----------+
| id | location |
+----+-----------+
| 1 | location1 |
| 2 | location2 |
| 3 | location3 |
+----+-----------+
Stops Table:
+----+---------+-------------+---------------------+
| id | team_id | location_id | timestamp |
+----+---------+-------------+---------------------+
| 1 | 10 | 2 | 2019-11-07 15:27:42 |
| 2 | 10 | 3 | 2019-11-07 16:37:52 |
| 3 | 10 | 4 | 2019-11-07 17:47:62 |
+----+---------+-------------+---------------------+
Looking to create the desired table:
+----+---------+---------------------+---------------------+---------------------+
| id | team_id | (loc id=2) | (loc id=3) | (loc id=4) |
+----+---------+---------------------+---------------------+---------------------+
| 1 | 10 | 2019-11-07 15:27:42 | 2019-11-07 16:37:52 | 2019-11-07 17:47:62 |
| 2 | 11 | | | |
| 3 | 12 | | | |
+----+---------+---------------------+---------------------+---------------------+
There will always be a finite number of locations.
Any guidance would be greatly appreciated! I've tried a handful of LEFT JOINS, but am not getting far.
You can do conditional aggregation:
select
t.id team_id
max(case when s.location_id = 2 then timestamp end) loc_id_2,
max(case when s.location_id = 3 then timestamp end) loc_id_3,
max(case when s.location_id = 4 then timestamp end) loc_id_4
from
team t
left join stops s on s.team_id = t.id
group by t.id
If you want to generate an id column on the fly for the generated results (which makes little sense since you get one record per team_id already), then you can use row_number() (availble in MySQL 8.0 onwards):
select
row_number() over(order by t.id) id,
t.*
from (
select
t.id team_id,
max(case when s.location_id = 2 then timestamp end) loc_id_2,
max(case when s.location_id = 3 then timestamp end) loc_id_3,
max(case when s.location_id = 4 then timestamp end) loc_id_4
from
team t
left join stops s on s.team_id = t.id
group by t.id
) t

Query to get most recent game results

I keep track of certain game results in a MySQL database. Now I want to print the latest results in a nice HTML table. I have three tables:
Table persons contains all participants of the game.
+-----+---------+
| id | name |
+-----+---------+
| 2 | Jon |
| 3 | Philip |
| 4 | Tom |
| 5 | Joey |
| 6 | Joanna |
+-----+---------+
The table rounds contains information about each round of the game. Among other things, the week in which the game was fought.
+-----+------+
| id | week |
+-----+------+
| 1 | 9 |
| 2 | 10 |
| 3 | 11 |
| 4 | 12 |
| 5 | 13 |
+-----+------+
And the table results contains the results for each person and round. The result column is a score in the game.
+------------+----------+--------+
| personId | roundId | result |
+------------+----------+--------+
| 2 | 1 | 2 |
| 4 | 1 | 6 |
| 5 | 1 | 6 |
| 3 | 1 | 10 |
| 2 | 2 | 16 |
| 4 | 2 | 14 |
| 5 | 2 | 5 |
| 3 | 2 | 11 |
+------------+----------+--------+
Now I want to print a table with the players scores each week. I want my output to look like the table below. Note that if a player did not participate one week, the cell should be empty.
+------+-----+--------+-----+------+--------+
| Week | Jon | Philip | Tom | Joey | Joanna |
+------+-----+--------+-----+------+--------+
| 9 | 2 | 10 | 6 | 6 | |
| 10 | 16 | 11 | 14 | 5 | |
+------+-----+--------+-----+------+--------+
So my question is: How do I do to get such output?
This is not a duplicate. See comments below.
So as stated in comments, you want to make a PIVOT, but MySQL does not support it.
However since your number of players is low and fixed, you can hardcode the players in a GROUP BY query like this :
SELECT R.Week,
SUM(CASE WHEN P.name = 'Jon' THEN S.result END) AS Jon,
SUM(CASE WHEN P.name = 'Philip' THEN S.result END) AS Philip,
SUM(CASE WHEN P.name = 'Tom' THEN S.result END) AS Tom,
SUM(CASE WHEN P.name = 'Joey' THEN S.result END) AS Joey,
SUM(CASE WHEN P.name = 'Joanna' THEN S.result END) AS Joanna
FROM persons P
LEFT JOIN results S ON P.id=S.personId
LEFT JOIN rounds R ON R.id=S.roundId
WHERE R.week IS NOT NULL
GROUP BY R.Week
SqlFiddleDemo

MySQL Grouping Query

I have a number of tables in my database.
Table: ObjectToPerson
For example if I had a number of entries below in the database:
+----+------------+------------+----------+----------+--------------+
| Id | WeekNumber | Date | PersonId | ObjectId | ObjectTypeId |
+----+------------+------------+----------+----------+--------------+
| 1 | 1 | 2015-11-04 | 1 | 1 | 1 |
| 2 | 1 | 2015-11-04 | 1 | 3 | 2 |
| 3 | 1 | 2015-11-04 | 2 | 2 | 1 |
| 4 | 1 | 2015-11-04 | 2 | 4 | 2 |
+----+------------+------------+----------+----------+--------------+
I am wanting to return the results back as two lines as follows:
+------+------------+----------+----------------------------+----------------------------+
| Week | Date | PersonId | ObjectId(ObjectTypeId = 1) | ObjectId(ObjectTypeId = 2) |
+------+------------+----------+----------------------------+----------------------------+
| 1 | 2015-11-04 | 1 | 1 | 3 |
| 1 | 2015-11-04 | 2 | 2 | 4 |
+------+------------+----------+----------------------------+----------------------------+
I am thinking of some sort of Group By query but I just can't seem to get it right.
Select * From ObjectToPerson
Left Join Objects O On O.Id = ObjectToPerson.ObjectId And ObjectToPerson.ObjectTypeId = 1
Left Join Objects O On O.Id = ObjectToPerson.ObjectId And ObjectToPerson.ObjectTypeId = 2
Can someone explain how I would get to this please?
You could use CASE to only select the ObjectId if the type is correct for the column, then use MAX/GROUP BY to group the result into a single row per person/week/date.
SELECT WeekNumber week, date, personid,
MAX(CASE WHEN ObjectTypeId=1 THEN ObjectId END) Type1,
MAX(CASE WHEN ObjectTypeId=2 THEN ObjectId END) Type2
FROM ObjectToPerson
GROUP BY week, date, personid
An SQLfiddle to test with.
You don't want two joins, you want a WHERE clause;
SELECT * FROM ObjectToPerson
LEFT JOIN Objects O ON O.Id = ObjectToPerson.ObjectId
WHERE ObjectToPerson.ObjectTypeId IN(1,2)