mysql join 3 tables by id - mysql

I have 3 tables to join and need some help to make it work, this is my schema:
donations:
+--------------------+------------+
| uid | amount | date |
+---------+----------+------------+
| 1 | 20 | 2013-10-10 |
| 2 | 5 | 2013-10-03 |
| 2 | 50 | 2013-09-25 |
| 2 | 5 | 2013-10-01 |
+---------+----------+------------+
users:
+----+------------+
| id | username |
+----+------------+
| 1 | rob |
| 2 | mike |
+----+------------+
causes:
+--------------------+------------+
| id | uid | cause | <missing cid (cause id)
+---------+----------+------------+
| 1 | 1 | stop war |
| 2 | 2 | love |
| 3 | 2 | hate |
| 4 | 2 | love |
+---------+----------+------------+
Result I want (data cropped for reading purposes)
+---------+-------------+---------+-------------+
| id | username | amount | cause |
+---------+-------------+---------+-------------+
| 1 | rob | 20 | stop war |
| 2 | mike | 5 | love |
+---------+-------------+-----------------------+
etc...
This is my current query, but returns double data:
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid)
EDIT: fixed sql schema on fiddle
http://sqlfiddle.com/#!2/0e06c/1 schema and data
How I can do this?

It seems your table's model is not right. There should be a relation between the Causes and Donations.
If not when you do your joins you will get duplicated rows.
For instance. Your model could look like this:
Donations
+--------------------+------------+
| uid | amount | date | causeId
+---------+----------+------------+
| 1 | 20 | 2013-10-10 | 1
| 2 | 5 | 2013-10-03 | 2
| 2 | 50 | 2013-09-25 | 3
| 2 | 5 | 2013-10-01 | 2
+---------+----------+------------+
causes:
+----------------------+
| id | cause |
+---------+------------+
| 1 | stop war |
| 2 | love |
| 3 | hate |
+---------+------------+
And the right query then should be this
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.id = tti.causeId)

Try this
SELECT CONCAT(i.username ,' ',i.first_name) `name`,
SUM(tti.amount),
t.cause AS tag_name
FROM users i
LEFT JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid)
GROUP BY i.id
Fiddle

You need to match the id from both the users and causes table at the same time, like so:
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid and t.id = i.id)
Apologies for formatting, I'm typing this on a phone.

Related

COUNT rows in 3 tables including zero values with MySQL

I have a MySQL 5.6 database with 3 tables:
job_offer
+----+----------+-------------+-----------+
| id | name | position_id | status_id |
+----+----------+-------------+-----------+
| 1 | John | 1 | 4 |
| 2 | Smith | 1 | 4 |
| 3 | Williams | 2 | 2 |
+----+----------+-------------+-----------+
position
+----+----------+
| id | name |
+----+----------+
| 1 | frontend |
| 2 | backend |
+----+----------+
status
+----+-----------+
| id | name |
+----+-----------+
| 1 | contacted |
| 2 | declined |
| 3 | rejected |
| 4 | interview |
+----+-----------+
I would like to build a query that can count all job offers by their position and statuses.
I have this query that performs almost the way I want it:
SELECT
position.name AS position_name,
status.name AS status_name
COUNT(job_offer.id) AS offers
FROM
job_offer
LEFT OUTER JOIN
position
ON job_offer.position_id = position.id
LEFT OUTER JOIN
status
ON job_offer.status_id = status.id
GROUP BY
position_name, status_name
Which gives me this result:
+---------------+-------------+--------+
| position_name | status_name | offers |
+---------------+-------------+--------+
| frontend | interview | 2 |
| backend | declined | 1 |
+---------------+-------------+--------+
The only problem is that I also need to display all existing statuses related to positions regardless of being NULL. So ideally it should look like this:
+---------------+-------------+--------+
| position_name | status_name | offers |
+---------------+-------------+--------+
| frontend | contacted | 0 |
| frontend | declined | 0 |
| frontend | rejected | 0 |
| frontend | interview | 2 |
| backend | contacted | 0 |
| backend | declined | 1 |
| backend | rejected | 0 |
| backend | interview | 0 |
+---------------+-------------+--------+
Is it possible to achieve this with one query? Thanks in advance for any help.
We can use a cross join approach between the position and status table to generate all possible combinations. Then, left join to job_offer and aggregate by position and status to find the counts:
SELECT
p.name AS position_name,
s.name AS status_name,
COUNT(jo.id) AS offers
FROM position p
CROSS JOIN status s
LEFT JOIN job_offer jo
ON jo.position_id = p.id AND
jo.status_id = s.id
GROUP BY
p.name,
s.name
ORDER BY
p.name,
s.name;
Demo

How can I get a total count of items?

I'm trying to figure out a MYSQL string and my noob-ness is getting in my way. I'm trying to count the total number of teams per phase.
Tables to consider:
phases
+----+------------+
| id | phase_name |
+----+------------+
| 1 | start |
| 2 | middle |
| 3 | end |
| 4 | finish |
+----+------------+
teams
+----+-----------+----------+
| id | team_name | phase_id |
+----+-----------+----------+
| 1 | team1 | 2 |
| 2 | team2 | 3 |
| 3 | team3 | 3 |
| 4 | team4 | 4 |
| 4 | team5 | 3 |
+----+-----------+----------+
Desired result
+----------+------------+-----------+
| phase_id | phase_name | tot_teams |
+----------+------------+-----------+
| 1 | start | NULL |
| 2 | middle | 1 |
| 3 | end | 3 |
| 4 | finish | 1 |
+----------+------------+-----------+
I've tried:
SELECT
T.phase_id, P.phase_name, COUNT(*) AS tot_teams
FROM
teams T
LEFT JOIN
phases P ON P.id = T.phase_id
GROUP BY
phase_id;
but that only shows the affected phase_id's...and I'm hoping to get ALL phase_id's in a table. I also tried:
SELECT
P.phase_name, T.phase_id, COUNT(*)
FROM
teams T
RIGHT JOIN
phases P on P.`id` = T.`phase_id`
GROUP BY
P.id
but that shows invalid data. (For example, phase_id has a qty of 1 but doesn't show up in the teams table.
Can you point me in the right direction?
Thanks!
The RIGHT JOIN is correct, but you need to use COUNT(T.phase_id) instead of COUNT(*). Otherwise, you're counting the row containing NULL that's generated for the phase with no teams.
Most people prefer to use LEFT JOIN, putting the master table first.
SELECT P.phase_name, P.phase_name, COUNT(T.phase_id)
FROM phase AS P
LEFT JOIN teams AS T ON P.id = T.phase_id
GROUP BY P.id

MySQL Count Comma Delimited

I have 3 tables like this:
table_events
+------+----------+----------------------+
| ID | Title | Employees |
+------+----------+----------------------+
| 1 | Event1 | john,james |
+------+----------+----------------------+
| 2 | Event2 | sarah,jessica |
+------+----------+----------------------+
table_check_in
+------+----------+----------+---------------------+
| ID | Time | EventID | By |
+------+----------+----------+---------------------+
| 1 | 08:30 | 1 | john |
+------+----------+----------+---------------------+
| 2 | 08:30 | 1 | james |
+------+----------+----------+---------------------+
| 3 | 09:30 | 1 | john |
+------+----------+----------+---------------------+
| 4 | 10:30 | 2 | sarah |
+------+----------+----------+---------------------+
| 5 | 10:35 | 2 | sarah |
+------+----------+----------+---------------------+
table_problems
+------+----------------+----------+---------------------+
| ID | Comment | EventID | By |
+------+----------------+----------+---------------------+
| 1 | Broken door | 1 | john |
+------+----------------+----------+---------------------+
| 2 | Slippery floor | 1 | john |
+------+----------------+----------+---------------------+
| 3 | Leaking tap | 1 | john |
+------+----------------+----------+---------------------+
| 4 | Broken window | 2 | jessica |
+------+----------------+----------+---------------------+
| 5 | Broken glass | 2 | jessica |
+------+----------------+----------+---------------------+
I would like to print something like this:
+------+----------+---------------+-------------------+-------------------+
| ID | Title | Employees | Count_Check_In | Count_Problems |
+------+----------+---------------+-------------------+-------------------+
| 1 | Event1 | john,james | john:2,james:1 | john:3,james:0 |
+------+----------+---------------+-------------------+-------------------+
| 2 | Event2 | sarah,jessica | sarah:2,jessica:0 | sarah:0,jessica:2 |
+------+----------+---------------+-------------------+-------------------+
I know this problem would be trivial if the database was designed properly, but we don't have the luxury of an application rewrite at the moment.
You need to initially get all the employees for each event id from check in and problem tables by using a union.
Then left join the counts from each of check in and problems table to the previous result to get the 0 counts as well.
Finally use a group_concat to get the result in one row for each event id.
select te.id,te.title,te.employees
,group_concat(concat(t.`By`,':',coalesce(tccnt.cnt,0))) count_check_in
,group_concat(concat(t.`By`,':',coalesce(tpcnt.cnt,0))) count_problems
from table_events te
left join (select eventid,`By` from table_check_in
union
select eventid,`By`from table_problems) t on te.id = t.eventid
left join (select eventid,`By`,count(*) cnt from table_check_in group by eventid,`By`) tccnt on tccnt.eventid = t.eventid and tccnt.`By`=t.`By`
left join (select eventid,`By`,count(*) cnt from table_problems group by eventid,`By`) tpcnt on tpcnt.eventid = t.eventid and tpcnt.`By`=t.`By`
group by te.id,te.title,te.employees
Sample Demo (thanks to #valex for setting up the schema)
You can use GROUP_CONCAT to get a result. Here is an example. The only thing missed is employees with 0 check ins or problems.
SELECT ID, Title,Employees,
GROUP_CONCAT(DISTINCT CONCAT(check_in.`By`,':',check_in.cnt))
as Count_Check_In,
GROUP_CONCAT(DISTINCT CONCAT(problems.`By`,':',problems.cnt))
as Count_Problems
FROM table_events
LEFT JOIN (SELECT EventID,`By`, COUNT(*) as cnt
FROM table_check_in
GROUP BY EventID,`By`) as check_in
ON table_events.ID = check_in.EventID
LEFT JOIN (SELECT EventID,`By`, COUNT(*) as cnt
FROM table_problems
GROUP BY EventID,`By`) as problems
ON table_events.ID = problems.EventID
GROUP BY table_events.id
Demo

join with count on a joined table with group clause in mysql

I have 3 tables:
applications (has many votes)
votes (belongs to applications and questions)
questions (has many votes)
I need to get number of votes per application per question.
So, my attempt was:
SELECT applications.id, COUNT(votes.id), votes.question_id
FROM applications
LEFT OUTER JOIN votes ON (votes.application_id = application.id)
GROUP BY votes.question_id
However, it displays data only for a single application, so I assume my query is malformed:
+----+-----------------+-------------+
| id | COUNT(votes.id) | question_id |
+----+-----------------+-------------+
| 1 | 1185 | 1 |
| 1 | 1170 | 2 |
| 1 | 1209 | 3 |
| 1 | 1230 | 4 |
| 1 | 1213 | 5 |
+----+-----------------+-------------+
What I need:
+----+-----------------+-------------+
| id | COUNT(votes.id) | question_id |
+----+-----------------+-------------+
| 1 | 1185 | 1 |
| 1 | 1170 | 2 |
| 1 | 1209 | 3 |
| 1 | 1230 | 4 |
| 1 | 1213 | 5 |
| 2 | null | 1 |
| 2 | 50 | 2 |
| 2 | 333 | 3 |
| 2 | 1230 | 4 |
| 2 | 1213 | 5 |
| 3 | null | 1 |
| 3 | 50 | 2 |
| 3 | 333 | 3 |
| 3 | null | 4 |
| 3 | 5555 | 5 |
+----+-----------------+-------------+
The group by clause was missing applications.id.
SELECT applications.id, COUNT(votes.id), votes.question_id
FROM applications
LEFT OUTER JOIN votes ON votes.application_id = application.id
group by applications.id, votes.question_id
You should be grouping by the applications.id as well as the questions.id:
SELECT a.id, COUNT(votes.id), votes.question_id
FROM applications a LEFT OUTER JOIN
votes v
ON v.application_id = a.id
GROUP BY a.id, v.question_id;
However, this will not produce exactly what you want. You seem to want all the questions for the applications, regardless of whether or not there are any votes. If so, this is probably what you want:
SELECT a.id, q.question_id, COUNT(v.application_id)
FROM applications a CROSS JOIN
(SELECT DISTINCT question_id FROM votes) q LEFT JOIN
votes v
ON v.application_id = a.id and v.question_id = q.question_id
GROPU BY a.id, q.question_id;

What Joins to use to join multiple tables together into one view

I have a db in mysql with multiple tables and would like to join multiple tables into one view to save me from having to build 3 or 4 sql statements or even one large joining statement in php to get the same info.
Here are all my tables that I want to join
track_title
+----+------------------+
| ID | TITLE |
+----+------------------+
| 1 | Title Here |
| 2 | Another Title |
| 3 | Some Other Title |
+----+------------------+
track_artist
+----+----------------+-----------+-----------+
| ID | TRACK_TITLE_ID | ARTIST_ID | SYMBOL_ID |
+----+----------------+-----------+-----------+
| 1 | 1 | 1 | 2 |
| 2 | 1 | 2 | 1 |
| 3 | 3 | 1 | 1 |
+----+----------------+-----------+-----------+
artist
+----+-------------+
| ID | ARTIST |
+----+-------------+
| 1 | Linkin Park |
| 2 | Metallica |
+----+-------------+
symbol
+----+--------+
| ID | SYMBOL |
+----+--------+
| 1 | |
| 2 | Feat. |
+----+--------+
tracklisting
+----+----------+----------+---------------+---------+
| ID | TRACK NO | TITLE_ID | VERSION | DISC NO |
+----+----------+----------+---------------+---------+
| 1 | 1 | 1 | | 1 |
| 2 | 1 | 2 | Album Version | 1 |
| 3 | 1 | 3 | Live Version | 1 |
+----+----------+----------+---------------+---------+
This is the final view I'm looking for
+----+----------+------------------+---------------+-----------------------------+---------+
| ID | TRACK NO | TITLE | VERSION | ARTIST | DISC NO |
+----+----------+------------------+---------------+-----------------------------+---------+
| 1 | 1 | Title Here | | Linkin Park Feat. Metallica | 1 |
| 2 | 1 | Another Title | Album Version | | 1 |
| 3 | 1 | Some Other Title | Live Version | Linkin Park | 1 |
+----+----------+------------------+---------------+-----------------------------+---------+
I have been bashing my head for the past 3 days with left, right, join and full join and just can't seem to get this to work.
Basically what I want to happen is the track_artist table will get the artist and symbol form the respective tables and concat them together into one column. Then join title and the concat column to have this view.
full_artist_view
+----------+------------------+-----------------------------+
| TITLE_ID | TITLE | FULL_ARTIST |
+----------+------------------+-----------------------------+
| 1 | Title Here | Linkin Park Feat. Metallica |
| 2 | Another Title | |
| 3 | Some Other Title | Linkin Park |
+----------+------------------+-----------------------------+
I have gotten this far but when I try join this to the tracklisting table I seem to crash my server which is getting very painful. No mysql error so I'm guessing I'm using the wrong join or this is just not possible.(though I can't see how this is not possible)
The tracklisting table is continuesly growing every week by 1000 records comfortable and is sitting at +- 75000 records.
To me this is the sql that should work, but doesn't
FROM full_artist_view LEFT JOIN tracklisting ON
full_artist_view.TITLE_ID = tracklisting.TITLE_ID
While I have only your small data sample and I can't see what your full_artist_view code looks like. You should be able to get the result using the following:
select tt.id,
tl.`track no`,
tt.title,
coalesce(tl.version, '') version,
group_concat(concat(coalesce(a.artist, ''), ' ', coalesce(s.symbol, '')) order by a.artist SEPARATOR ' ') artist,
tl.`disc no`
from track_title tt
inner join tracklisting tl
on tt.id = tl.TITLE_ID
left join track_artist ta
on tt.id = ta.TRACK_TITLE_ID
left join artist a
on ta.artist_id = a.id
left join symbol s
on ta.symbol_id = s.id
group by tt.id, tl.`track no`, tt.title, tl.version, tl.`disc no`
See SQL Fiddle with Demo. This returns:
| ID | TRACK NO | TITLE | VERSION | ARTIST | DISC NO |
---------------------------------------------------------------------------------------------
| 1 | 1 | Title Here | | Linkin Park Feat. Metallica | 1 |
| 2 | 1 | Another Title | Album Version | | 1 |
| 3 | 1 | Some Other Title | Live Version | Linkin Park | 1 |
Quick play, and I think (but not certain) it is something like this that you need:-
SELECT a.Id, a.Track_No, b.Title, a.Version, GROUP_CONCAT(CONCAT(d.Artists, e.Symbol)), a.Disc_No
FROM tracklisting a
INNER JOIN track_title b ON a.Title_id = b.Id
LEFT OUTER JOIN track_artist c ON b.Id = c.Track_Title_Id
LEFT OUTER JOIN artist d ON c.Artist_Id = d.Id
LEFT OUTER JOIN symbol e ON d.SymbolId = e.Id
GROUP BY a.Id, a.Track_No, b.Title, a.Version, a.Disc_No
But I might have missed something so let me know!