Select/Join multiple fields from different tables with same column name - mysql

I have one key table, a number of data tables with same column names in them, and one users table. I am trying to select values from the key table, join this output with some selected values from each of the data tables by unique ID (uaID) to the entries selected already from the key table (each key table result will have a relative join from only one of the range of data tables, not all of them and some entries will return a null result from the data tables and we don't want this to break anything or be omitted because of the null result), and then finally join some user data to each result from the users table. This will always have a result.
Let me "draw" a basic version of my tables so you can see.
keyTable
-----------------------------------------
| uaID | userID | key | appName |
|---------------------------------------|
| 1 | 7 | ABC01 | Physics |
| 2 | 9 | DEF20 | Geometry |
| 3 | 12 | XJG14 | Biology |
| 4 | 19 | DAF09 | Chemistry |
| 5 | 27 | KYT78 | Algebra |
| 6 | 29 | PLF43 | Statistics|
| 7 | 34 | COG89 | Geology |
| 8 | 45 | HYL72 | Art |
| 9 | 48 | TSK45 | History |
| 10 | 53 | BBS94 | GeoChem |
| 11 | 59 | DOD10 | BioChem |
| 12 | 27 | HKV62 | Music |
-----------------------------------------
dataTable01
-----------------------------------------------
| uaID | sector | subSector | topic |
|---------------------------------------------|
| 2 | circle | volumn | measure |
| 7 | triangle | hypotenuse |pythagoras |
| 8 | square | | |
| 11 | triangle | hypotenuse |pythagoras |
-----------------------------------------------
dataTable02
---------------------
| uaID | topic |
|-------------------|
| 1 | door |
| 3 | window |
| 9 | porch |
| 12 | |
---------------------
dataTable03
-----------------------------------------------
| uaID | sector | subSector | topic |
|---------------------------------------------|
| 4 | cat | feline | kitty |
| 5 | dog | canine | rover |
| 6 | kangaroo | marsupial | jack |
| 10 | bunny | leporidae | peter |
-----------------------------------------------
users
------------------------------------------------------------------------
| userID | Title | firstName | lastName | email |
|----------------------------------------------------------------------|
| 7 | Dr | Melissa | Smith | mel#email.com |
| 9 | Mr | Bob | Andrews | bob#email.com |
| 12 | Miss | Clare | Greco | clare#email.com |
| 19 | Mr | Dan | Fonseca | dan#email.com |
| 27 | Mr | Matt | Jones | matt#email.com |
| 29 | Mr | Chris | Nimmo | chris#email.com |
| 34 | Mrs | Lisa | Araujo | lisa#email.com |
| 45 | Miss | Raquel | Bailey | raquel#email.com |
| 48 | Dr | Steven | Dowd | steven#email.com |
| 53 | Prof | Roger | Hesp | roger#email.com |
| 59 | Prof | Sally | Bryce | sally#email.com |
| 65 | Mrs | Elena | Eraway | elena#email.com |
------------------------------------------------------------------------
And this is what I am trying to achieve as the end result:
-------------------------------------------------------------------------------------------------------------------------------
| uaID | key | appName | sector | subSector | topic | title | firstName | lastName | email |
|-----------------------------------------------------------------------------------------------------------------------------|
| 1 | ABC01 | Physics | | | door | Dr | Melissa | Smith | mel#email.com |
| 2 | DEF20 | Geometry | circle | volumn | measure | Mr | Bob | Andrews | bob#email.com |
| 3 | XJG14 | Biology | | | window | Miss | Clare | Greco | clare#email.com |
| 4 | DAF09 | Chemistry | cat | feline | kitty | Mr | Dan | Fonseca | dan#email.com |
| 5 | KYT78 | Algebra | dog | canine | rover | Mr | Matt | Jones | matt#email.com |
| 6 | PLF43 | Statistics| kangaroo | marsupial | jack | Mr | Chris | Nimmo | chris#email.com |
| 7 | COG89 | Geology | triangle | hypotenuse |pythagoras | Mrs | Lisa | Araujo | lisa#email.com |
| 8 | HYL72 | Art | square | | | Miss | Raquel | Bailey | raquel#email.com |
| 9 | TSK45 | History | | | porch | Dr | Steven | Dowd | steven#email.com |
| 10 | BBS94 | GeoChem | bunny | leporidae | peter | Prof | Roger | Hesp | roger#email.com |
| 11 | DOD10 | BioChem | triangle | hypotenuse |pythagoras | Prof | Sally | Bryce | sally#email.com |
| 12 | HKV62 | Music | | | | Mr | Matt | Jones | matt#email.com |
-------------------------------------------------------------------------------------------------------------------------------
I am attempting to achieve this by executing:
$sql = "SELECT keyTable.uaID, keyTable.userID, keyTable.key,
keyTable.appName, dataTable01.sector, dataTable01.subSector,
dataTable01.topic, dataTable02.topic, dataTable03.sector,
dataTable03.subSector, dataTable03.topic, users.title,
users.firstName, users.lastName, users.email
FROM keyTable
LEFT OUTER JOIN dataTable01 ON keyTable.uaID = dataTable01.uaID
LEFT OUTER JOIN dataTable02 ON keyTable.uaID = dataTable02.uaID
LEFT OUTER JOIN dataTable03 ON keyTable.uaID = dataTable03.uaID
LEFT OUTER JOIN users ON keyTable.userID = users.userID";
I get all the keyTable data. I get all the users data right where it's supposed to join up all ok. I get all the dataTable03 data as well, but I do not get any data from dataTable01 or dataTable02 showing up in the result. If I omit the call to dataTable03 I then get all the relevant data from dataTable02 showing up, but no data from dataTable01. The call to the users table is at the end and always shows up fine. So clearly it's an issue with the matching field names in the data tables. I get no errors at all and the process completes, just with the mentioned data missing. I've tried different JOINS - INNER JOIN, OUTER JOIN, LEFT OUTER JOIN. There obviously has to be a way to achieve this but cannot seem to find any references on the web to this specific problem. Can someone tell me what I am doing incorrectly please?

After joining, you can use COALESCE to get the non-null value from the table with a matching row.
$sql = "SELECT k.uaID, k.userID, k.key, k.appName,
COALESCE(d1.sector, d3.sector, '') AS sector,
COALESCE(d1.subSector, d3.subSector, '') AS subSector,
COALESCE(d1.topic, d2.topic, d3.topic, '') AS topic,
users.title, users.firstName, users.lastName, users.email
FROM keyTable AS k
LEFT OUTER JOIN dataTable01 AS d1 ON k.uaID = d1.uaID
LEFT OUTER JOIN dataTable02 AS d2 ON k.uaID = d2.uaID
LEFT OUTER JOIN dataTable03 AS d3 ON k.uaID = d3.uaID
LEFT OUTER JOIN users ON k.userID = users.userID
ORDER BY k.uaID";
Another way to merge the data from the datatablesNN tables into the same column os tp use UNION.
SELECT k.uaID, k.userID, k.key, k.appName, IFNULL(d.sector, '') AS sector, IFNULL(d.subSector, '') AS subSector, IFNULL(d.topic, '') AS topic,
u.title, u.firstName, u.lastName, u.email
FROM keyTable AS k
LEFT OUTER JOIN (
SELECT uaID, sector, subSector, topic
FROM dataTable01
UNION
SELECT uaID, NULL, NULL, topic
FROM datatable02
UNION
SELECT uaID, sector, subSector, topic
FROM datatable03) AS d
ON k.uaID = d.uaID
LEFT JOIN users AS u ON u.userID = k.userID
ORDER BY k.uaID
DEMO

You would have to use aliases
simular issue and solution here:
php-mysql-how-to-resolve-ambiguous-column-names-in-join-operation
select * from common inner join (
(select link from table1)
union
(select link from table2)
) as unionT
on unionT.link = common.link

Related

MySQL select statement missing some fields

I have following statement that is used to select some fields from MySQL DB
select finance_budget_issue.budget_date, SUM(finance_budget_issue.amount) AS amount, finance_vote.office_id as vote_office_id, finance_office.office_head as head,
finance_office.office_name AS office_name,
finance_budget.ref_no, finance_budget_issue.view_status, tbl_signature.office_head as sign_office_head, tbl_signature.name AS name,
tbl_signature.post AS post, tbl_signature.sign_id
from finance_budget_issue
inner join finance_budget on finance_budget.budget_id=finance_budget_issue.budget_id
left join finance_vote on finance_budget_issue.vote_id=finance_vote.vote_id
left join finance_vote_description on finance_vote.description=finance_vote_description.vote_description_id
left join finance_office on finance_budget_issue.office=finance_office.office_id
left join tbl_signature on finance_office.office_id=tbl_signature.office_id
The statement is working fine, but didn't outs the following fields
tbl_signature.office_head as sign_office_head,
tbl_signature.name AS name,
tbl_signature.post AS post
What may be going wrong ? I think that I used incorrect Joins. Can anyone help ?
Tables as follows :
finance_office
+----+-----------+-------------+------+
| id | office_id | office_name | head |
+----+-----------+-------------+------+
| 1 | 48 | A | SS |
| 2 | 69 | B | VV |
+----+-----------+-------------+------+
finance_vote
+---------+-----------+----------------+
| vote_id | office_id | vote |
+---------+-----------+----------------+
| 1 | 48 | 320-1-2-1-1001 |
| 2 | 48 | 320-2-2-2-2002 |
| 3 | 69 | 319-1-2-1-1001 |
| 4 | 69 | 319-1-2-2-1102 |
| 5 | 30 | 318-1-1-2-1101 |
+---------+-----------+----------------+
tbl_signature
+---------+-----------+---------+------------+-------------+
| sign_id | office_id | name | post | office_head |
+---------+-----------+---------+------------+-------------+
| 1 | 48 | Noel | Accountant | Manager |
| 2 | 69 | Jhon | Accountant | Manager |
| 3 | 30 | Micheal | Accountant | Manager |
+---------+-----------+---------+------------+-------------+
finance_budget
+-----------+--------+-------------+
| budget_id | ref_no | budget_date |
+-----------+--------+-------------+
| 1 | Acc/01 | 2020-01-20 |
| 2 | Acc/02 | 2020-01-22 |
+-----------+--------+-------------+
finance_budget_issue
+----+-----------+--------+---------------+-----------------+
| id | budget_id | amount | budget_status | transfer_status |
+----+-----------+--------+---------------+-----------------+
| 1 | 1 | 75000 | issues | Approved |
| 2 | 1 | 22000 | issues | Approved |
| 3 | 2 | 65000 | issues | Approved |
+----+-----------+--------+---------------+-----------------+
Desired Output
+--------+----------------+------+--------+------------------+------+------------+
| amount | vote_office_id | head | ref_no | sign_office_head | name | post |
+--------+----------------+------+--------+------------------+------+------------+
| 75000 | 48 | SS | Acc/01 | Manager | Noel | Accountant |
| 22000 | 48 | SS | Acc/01 | Manager | Noel | Accountant |
| 65000 | 69 | VV | Acc/02 | Manager | Jhon | Accountant |
+--------+----------------+------+--------+------------------+------+------------+
Generated Output (Incorrect)
+--------+----------------+------+--------+------------------+------+------+
| amount | vote_office_id | head | ref_no | sign_office_head | name | post |
+--------+----------------+------+--------+------------------+------+------+
| 75000 | 48 | SS | Acc/01 | | | |
| 22000 | 48 | SS | Acc/01 | | | |
| 65000 | 69 | VV | Acc/02 | | | |
+--------+----------------+------+--------+------------------+------+------+
This is easier to read:
SELECT i.budget_date
, SUM(i.amount) amount
, v.office_id vote_office_id
, o.office_head head
, o.office_name
, b.ref_no
, i.view_status
, s.office_head sign_office_head
, s.name
, s.post
, s.sign_id
FROM finance_budget_issue i
JOIN finance_budget b
ON b.budget_id = i.budget_id
LEFT
JOIN finance_vote v
ON v.vote_id = i.vote_id
LEFT
JOIN finance_vote_description d
ON d.vote_description_id = v.description
LEFT
JOIN finance_office o
ON i.office = o.office_id
LEFT
JOIN tbl_signature s
ON s.office_id = o.office_id
You have an aggregate function (and non-aggregated columns) but no GROUP BY clause; that's not going to work. You have a LEFT JOINed table from which you select no columns; that's pointless.
For further help, see Why should I provide an MCRE for what seems to me to be a very simple SQL query

How to update a column to match data from another column in the same table

I'm trying to get a query going that would update the parent_id row to reflect the corresponding username that possesses the same member_id as the parent_id.
Below is a representation of the db_name.members table, and I want to convert the parent_ids into their corresponding usernames.
+--------+----------+-----------+-----------+
| name | username | member_id | parent_id |
+--------+----------+-----------+-----------+
| Jeff | Jeff | 167 | NULL |
| Asia | Asia | 143 | NULL |
| Bogart | Bogart | 389 | 167 |
| Greg | Greg | 894 | NULL |
| Hatsy | Hatsy | 328 | 167 |
| Super | Super | 721 | NULL |
| Goku | Goku | 534 | 894 |
| Banana | Banana | 520 | NULL |
| Kyle | Kyle | 456 | 520 |
+--------+----------+-----------+-----------+
What it should look like after:
+--------+----------+-----------+-----------+
| name | username | member_id | parent_id |
+--------+----------+-----------+-----------+
| Jeff | Jeff | 167 | NULL |
| Asia | Asia | 143 | NULL |
| Bogart | Bogart | 389 | Jeff |
| Greg | Greg | 894 | NULL |
| Hatsy | Hatsy | 328 | Jeff |
| Super | Super | 721 | NULL |
| Goku | Goku | 534 | Greg |
| Banana | Banana | 520 | NULL |
| Kyle | Kyle | 456 | Banana |
+--------+----------+-----------+-----------+
This is my current (not working) query:
UPDATE members SET parent_id=(SELECT name FROM members WHERE member_id=parent_id);
You can use this, using a UPDATE with INNER JOIN:
UPDATE members m1 INNER JOIN members m2 ON m1.parent_id = m2.member_id
SET m1.parent_id = m2.username
Keep in mind that in case the parent_id column is a integer / numeric column you can't set the username to this column. You can use a SELECT instead of a UPDATE:
SELECT m1.name, m1.username, m1.member_id, m2.username AS parent_username
FROM members m1 LEFT JOIN members m2 ON m1.parent_id = m2.member_id
demo on dbfiddle.uk

Remove duplicate rows using MIN() function with GROUP BY and INNER JOIN

I am trying to list the unique/distinct MIN(time) for each person in the 'Results table' while joining the 'Athletes table' but I am getting duplicates.
Here is some sample data (I am running MySql 5.7)
Results Table
+----------+-----------+---------+----------+-------+-------------+------------+
| resultID | athleteID | eventID | ageGroup | time | venue | date |
+----------+-----------+---------+----------+-------+-------------+------------+
| 1 | 10 | 1 | MS | 10.20 | Tokyo | 06-06-2019 |
| 2 | 11 | 1 | MS | 10.24 | London | 03-08-2019 |
| 3 | 10 | 1 | MS | 10.20 | Los Angeles | 01-11-2019 |
| 4 | 13 | 1 | MS | 10.29 | Glasgow | 28-10-2019 |
| 5 | 14 | 1 | MS | 10.32 | Oslo | 16-07-2019 |
| ... | ... | ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... | ... | ... |
+----------+-----------+---------+----------+-------+-------------+------------+
Athletes Table
+-----------+-----------+----------+--------+-------------+
| athleteID | nameFirst | nameLast | gender | dateOfBirth |
+-----------+-----------+----------+--------+-------------+
| 10 | Bill | Smith | MS | 10-11-2000 |
| 11 | John | Brown | MS | 1-08-1999 |
| 12 | Steve | Jones | MS | 16-01-1997 |
| 13 | Alan | Green | MS | 21-07-2001 |
| 14 | Paul | Black | MS | 27-10-2000 |
| ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... |
+-----------+-----------+----------+--------+-------------+
I have tried the following code - which appears to bring the correct results set, but returns duplicate values. Bill Smith ran 10.20 twice but I only need to show one of them.
Have tried using the DISTINCT function on both SELECT's but no luck - so this is what I have:
SELECT *
FROM results
INNER JOIN (
SELECT athleteID, nameFirst, nameLast, MIN(time) as minTime
FROM results
INNER JOIN athletes USING(athleteID)
WHERE eventID = '1'
AND ageGroup IN('MS')
AND YEAR(results.date) = '2019'
GROUP BY athleteID
) AS child ON (results.athleteID = child.athleteID) AND (results.time = minTime)
HAVING YEAR(results.date) = '2019'
ORDER BY minTime ASC
I get this result
+-------+-----------+----------+-------------+------------+
| time | nameFirst | nameLast | venue | date |
+-------+-----------+----------+-------------+------------+
| 10.20 | Bill | Smith | Tokyo | 06-06-2019 |
| 10.20 | Bill | Smith | Los Angeles | 01-11-2019 |
| 10.24 | John | Brown | London | 03-08-2019 |
| 10.29 | Steve | Jones | Glasgow | 28-10-2019 |
| 10.32 | Alan | Green | Oslo | 16-07-2019 |
| ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... |
+-------+-----------+----------+-------------+------------+
As you can see, the additional result for Bill Smith (10.20 - Los Angeles) is also showing up. I need this to be omitted and only show 1 result per athlete - as below.
Desired Result
+-------+-----------+----------+---------+------------+
| time | nameFirst | nameLast | venue | date |
+-------+-----------+----------+---------+------------+
| 10.20 | Bill | Smith | Tokyo | 06-06-2019 |
| 10.24 | John | Brown | London | 03-08-2019 |
| 10.29 | Steve | Jones | Glasgow | 28-10-2019 |
| 10.32 | Alan | Green | Oslo | 16-07-2019 |
+-------+-----------+----------+---------+------------+
Any suggestions as to what I could try?
Many thanks in advance ..
You have athlete with the same min time in this case you need the min date too in outer select
SELECT r.athleteID, r.nameFirst, r.nameLast, min(r.date), child.minTime
FROM results r
INNER JOIN (
SELECT athleteID, nameFirst, nameLast
, MIN(time) as minTime
FROM results
INNER JOIN athletes USING(athleteID)
WHERE eventID = '1'
AND ageGroup IN('MS')
AND YEAR(results.date) = '2019'
GROUP BY athleteID
) AS child ON (r.athleteID = child.athleteID) AND (r.time = minTime)
WHERE YEAR(r.date) = '2019'
GROUP BY r.athleteID, child.minTime
ORDER BY minTime ASC

Inequality in Mysql with count()

I have the following structure :
Table Author :
idAuthor,
Name
+----------+-------+
| idAuthor | Name |
+----------+-------+
| 1 | Renee |
| 2 | John |
| 3 | Bob |
| 4 | Bryan |
+----------+-------+
Table Publication:
idPublication,
Title,
Type,
Date,
Journal,
Conference
+---------------+--------------+------+-------------+------------+-----------+
| idPublication | Title | Date | Type | Conference | Journal |
+---------------+--------------+------+-------------+------------+-----------+
| 1 | Flower thing | 2008 | book | NULL | NULL |
| 2 | Bees | 2009 | article | NULL | Le Monde |
| 3 | Wasps | 2010 | inproceding | KDD | NULL |
| 4 | Whales | 2010 | inproceding | DPC | NULL |
| 5 | Lyon | 2011 | article | NULL | Le Figaro |
| 6 | Plants | 2012 | book | NULL | NULL |
| 7 | Walls | 2009 | proceeding | KDD | NULL |
| 8 | Juices | 2010 | proceeding | KDD | NULL |
| 9 | Fruits | 2010 | proceeding | DPC | NULL |
| 10 | Computers | 2010 | inproceding | DPC | NULL |
| 11 | Phones | 2010 | inproceding | DPC | NULL |
| 12 | Creams | 2010 | proceeding | DPC | NULL |
| 13 | Love | 2010 | proceeding | DPC | NULL |
+---------------+--------------+------+-------------+------------+-----------+
Table author_has_publication :
Author_idAuthor,
Publication_idPublication
+-----------------+---------------------------+
| Author_idAuthor | Publication_idPublication |
+-----------------+---------------------------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 1 | 5 |
| 2 | 5 |
| 3 | 5 |
| 3 | 6 |
| 4 | 7 |
| 4 | 8 |
| 4 | 9 |
| 4 | 10 |
| 3 | 11 |
| 3 | 12 |
| 2 | 13 |
+-----------------+---------------------------+
I want to obtain the list of all authors having published at least 2 times at conference DPC in 2010.
I achieved to get the list of autors that have published something, and the number of publication for each, but I can't get my 'at least 2' factor.
My following query
SELECT author.name, COUNT(name) FROM author INNER JOIN author_has_publication ON author.idAuthor=author_has_publication.Author_idAuthor INNER JOIN publication ON author_has_publication.Publication_idPublication=publication.idPublication AND publication.date=2010 AND publication.conference='DPC'GROUP BY author.name;
returns the following result (which is good)
+-------+-------------+
| name | COUNT(name) |
+-------+-------------+
| Bob | 2 |
| Bryan | 3 |
| John | 1 |
+-------+-------------+
but when I try to select only the one with a count(name)>=2, i got an error.
I tried this query :
SELECT author.name, COUNT(name) FROM author INNER JOIN author_has_publication ON author.idAuthor=author_has_publication.Author_idAuthor INNER JOIN publication ON author_has_publication.Publication_idPublication=publication.idPublication AND publication.date=2010 AND publication.conference='DPC'GROUP BY author.name WHERE COUNT(name)>=2;
When you use aggregation funcion you can filter with a proper operator named HAVING
Having worok on the result of the query (then pn the aggrgated result like count() ) instead of where that work on the original value of the tables rows
SELECT author.name, COUNT(name)
FROM author INNER JOIN author_has_publication
ON author.idAuthor=author_has_publication.Author_idAuthor
INNER JOIN publication
ON author_has_publication.Publication_idPublication=publication.idPublication
AND publication.date=2010 AND publication.conference='DPC'
GROUP BY author.name
HAVING COUNT(name)>=2;

MySQL query is returning duplicates when it shouldn't

I have three tables:
Person
+--------+-----------+
| fName | lName |
+--------+-----------+
| Paul | McCartney |
| John | Lennon |
| Jon | Stewart |
| Daniel | Tosh |
| Steven | Colbert |
| Pink | Floyd |
| The | Beatles |
| Arcade | Fire |
| First | Last |
| Andrew | Bird |
+--------+-----------+
Publication
+----+---------------------------------------+------+-----------+---------+
| id | title | year | pageStart | pageEnd |
+----+---------------------------------------+------+-----------+---------+
| 9 | The Dark Side of the Moon | 1973 | 0 | 0 |
| 10 | Piper At The Gates of Dawn | 1967 | 0 | 0 |
| 11 | Sgt. Pepper's Lonely Hearts Band Club | 1967 | 0 | 0 |
| 12 | Happy Thoughts | 2007 | 0 | 60 |
| 13 | Wish You Were Here | 1975 | 0 | 0 |
| 14 | Funeral | 2004 | 0 | 0 |
+----+---------------------------------------+------+-----------+---------+
Person_Publication
+-----------+----------------+--------+---------------+
| person_id | publication_id | editor | author_number |
+-----------+----------------+--------+---------------+
| 11 | 11 | 0 | 1 |
| 12 | 11 | 0 | 1 |
| 16 | 9 | 0 | 1 |
| 17 | 11 | 0 | 1 |
+-----------+----------------+--------+---------------+
I'm trying to select all authors of a certain publication using the following query:
SELECT fName , lName
FROM Publication , Person, Person_Publication
WHERE Person.id = Person_Publication.person_id
AND Person_Publication.publication_id = 11;
But the results I get are always duplicates (always 6x for some reason). The results:
+-------+-----------+
| fName | lName |
+-------+-----------+
| Paul | McCartney |
| John | Lennon |
| The | Beatles |
| Paul | McCartney |
| John | Lennon |
| The | Beatles |
| Paul | McCartney |
| John | Lennon |
| The | Beatles |
| Paul | McCartney |
| John | Lennon |
| The | Beatles |
| Paul | McCartney |
| John | Lennon |
| The | Beatles |
| Paul | McCartney |
| John | Lennon |
| The | Beatles |
+-------+-----------+
18 rows in set (0.03 sec)
Can somebody please tell me why this is happening and how to fix this?
You are getting 6x your results, exactly one for each Publication row.
Remove your Publication from your FROM clause:
SELECT fName , lName
FROM Person, Person_Publication
WHERE Person.id = Person_Publication.person_id
AND Person_Publication.publication_id = 11;
You are including three tables in your query:
FROM Publication, Person, Person_Publication
but you have only one join condition:
WHERE Person.id = Person_Publication.person_id
You end up with a cartesian product between Publication and Person JOIN Person_Publication
Add the following condition to your WHERE block:
AND Publication.id = Person_Publication.publication.id
A perfect example of why the explicit JOIN syntax is prefered. With the following syntax:
SELECT fName, lName
FROM Publication
JOIN Person_Publication ON Person_Publication.publication.id = Publication.id
JOIN Person ON Person.id = Person_Publication.person_id
WHERE Person_Publication.publication_id = 11;
.. such a mistake simply cannot happen.