MySQL Multiple Select Ambiguous Results - mysql

Using phpMyAdmin 5.1.44 to experiment with DML commands.
I've been following tutorials on-line.
SELECT book.b_isbn, publisher.p_name FROM 'book', 'publisher' WHERE book.b_title='DSA'
Table 1
book
b_id(PK) b_isbn b_title p_id(FK)
-----------------------------------------
1 12345 DSA 1
2 23456 SD 1
3 34567 CSP 2
Table 2
publisher
p_id(PK) p_name
--------------------
1 Fred
2 John
Expected Results
b_isbn p_name
---------------------
12345 Fred
Actual Results
b_isbn p_name
----------------------
12345 Fred
34567 John
Any ideas?

You need to tell MySQL how to join the tables together (without which it just matches every book to every publisher) - use any one of:
add AND publisher.p_id = book.p_id to your WHERE clause;
tell MySQL to join ON that condition / USING that column;
... FROM book JOIN publisher ON publisher.p_id = book.p_id WHERE ...
or
... FROM book JOIN publisher USING (p_id) WHERE ...
use a NATURAL JOIN to have MySQL guess that's what you want based on the column names.
... FROM book NATURAL JOIN publisher WHERE ...

I think you need to put the fk to the pk key in the where statement
SELECT
*
FROM
book, publisher
WHERE
book.p_id=publisher.p_id
AND book.b_title='DSA'
Or even better use JOINs:
SELECT
*
FROM
book
JOIN publisher
ON book.p_id=publisher.p_id
WHERE
book.b_title='DSA'
Or if you are not sure if there is a corresponding value then use a left join. Like this:
SELECT
*
FROM
book
LEFT JOIN publisher
ON book.p_id=publisher.p_id
WHERE
book.b_title='DSA'

Related

Binding results of two SQL queries by column

I have tried a lot of searching online and unable to find an answer to this. As an example, lets say I have two tables, Persons and Names with the following info (this is only for illustration and I don't have control over the DB structure)
Persons:
PID FNameID LNameID
1 100 101
2 102 103
3 100 103
...
Names:
NameID Name
100 James
101 Baker
102 Thomas
103 Walter
Is there a simple query to obtain the following result, for a given person (PID) the first and last names and then combine them to display like:
PID FName LName
1 James Baker
I looked at UNION, JOIN, nested queries, etc but couldn't figure it out. Feel like I'm missing something basic. Any help is appreciated.
Wanted to add that I am able to get each name separately and combine with PHP, but I was hoping there was a Sql way of doing it (like UNION does it for row binding)
You need two joins:
select p.pid, nf.name as fname, nl.name as lname
from persons p left join
names nf
on p.fnameid = nf.nameid left join
names nl
on p.lnameid = nl.nameid;
Try this
SELECT
p.PID, fname.Name as FName, lname.Name as LName
FROM Persons p
JOIN Name fname ON p.FNameId=fname.Id
JOIN Name lname ON p.LNameId=lname.Id
WHERE p.PID=1;
More about JOIN https://dev.mysql.com/doc/refman/8.0/en/join.html

MYSQL, is this kind of request possible?

I have persons (table person) who have 0 or N roles (tables role and personne_role).
I want to select all the persons , with the roles they have, to have this kind of result :
PHIL COLLINS | Drummer | Singer
MIKE RUTHERFORD | Singer
ION ANDERSON | Singer
MIKE JAGGER |
CARLOS SANTANA | Guitarist
......
Each line can have 0 or N roles.
To do that, I make 2 requests
the first one to get the employees (table person)
the second one to loop all the retrieved employees and retrieve each role of them (tables role and person_role)
It works BUT in the case of there are a lot of lines, it is not very efficient.
I would like the same result in 1 request.
Is it possible ?
What are the mysql keywords I must use to do that ?
Thanks for your feedback.
dominique
You could use a JOIN with a GROUP_CONCAT, something like:
SELECT person.name, role.roles
FROM person
LEFT JOIN (
SELECT person_id, GROUP_CONCAT(DISTINCT role SEPARATOR ' | ') roles
FROM person_role
GROUP BY person_id
) role ON (person.id = role.person_id)
EDIT: the fields name are just a guess, since you didn't show us the full table schema; also, if the roles are actually in a separate tale, say joined by a role_id, you'd need to add it to the subquery.

Ordering the results from a junction table based on values of the row its foreign key belongs to

I have three tables for a many-to-many: Authors, Authorships, Books. I would like to select rows from authorship and order it alphabetically according to the author this row belongs to.
Example:
-- Authors --
ID Name
1 Peter
2 Gregory
3 Daniel
-- Authorships--
ID AuthorId BookId
1 1 1
2 2 1
3 3 1
-- Books--
ID Name
1 Foobook
I would like to write a select statement that returns all rows from authorship belonging to a specific book then orders the result by author name.
So something like this:
SELECT * FROM Authorships WHERE BookId = 1 ORDER BY (Authors.Name???);
Except I need to order the result.
I understand how this question might look silly because of its workaround/inefficient nature, but I am working with a lot of legacy code and am not allowed to really change anything else.
Thank you.
This will work:
SELECT auth.* FROM Authorships auth, Authors au, Books bk WHERE auth.BookId = bk.ID and auth.AuthorId = au.ID ORDER BY au.Name
SQLFiddle Link:
SQLFiddle

MySQL select only new records

How to write a MySQL query to achieve this task?
Table: writers
w_id w_name
---------------
1 Michael
2 Samantha
3 John
---------------
Table: articles
a_id w_id timestamp a_name
----------------------------------------
1 1 0000000001 PHP programming
2 3 0000000003 Other programming languages
3 3 0000000005 Another article
4 2 0000000015 Web design
5 1 0000000020 MySQL
----------------------------------------
Need to SELECT only those writers who published their first article not earlier than 0000000005. (only writers who published at least one article can be selected)
In this example the result would be:
2 Samantha
SQL code can be tested here http://sqlfiddle.com/#!2/7a308
Untested, but close:
SELECT w_id, MIN(timestamp) as min_time
from writers w
JOIN articles a on w.w_id = a.w_id
GROUP BY 1
HAVING min_time > 5
Here's one approach, using an inline view (or "derived table" as MySQL calls it) to get the earliest timestamp for each writer:
SELECT w.w_id
, w.w_name
-- , e.earliest_timestamp
FROM writers w
LEFT
JOIN ( SELECT a.w_id
, MIN(a.timestamp) AS earliest_timestamp
FROM articles a
GROUP BY a.w_id
) e
ON e.w_id = w.w_id
WHERE e.earliest_timestamp >= '0000000005'
ORDER BY w.w_id
This may not be the most efficient approach, but you can run just the query in the inline view (aliased as e) to see what it returns. We can then reference the result set from that query like we do a table (with some restrictions.)
(Other approaches can make better use of suitable indexes.)
I'm unclear on the datatype of earliest_timestamp column. The SQL above assumes it's character datatype. If it's integer rather than character, the WHERE clause could look like this:
WHERE e.earliest_timestamp >= 5

find out count of comma based value in MySql

I have two tables.
Table Emp
id name
1 Ajay
2 Amol
3 Sanjay
4 Vijay
Table Sports
Sport_name Played by
Cricket ^2^,^3^,^4^
Football ^1^,^3^
Vollyball ^4^,^1^
Now I want to write a query which will give me output like
name No_of_sports_played
Ajay 2
Amol 1
Sanjay 2
Vijay 2
So what will be Mysql query for this?
I agree with the above answers/comments that you are not using a database for what a database is for, but here is how you could calculate your table from your current structure in case you have no control over that:
SELECT Emp.name, IF(Played_by IS NULL,0,COUNT(*)) as Num_Sports
FROM Emp
LEFT JOIN Sports
ON Sports.Played_by RLIKE CONCAT('[[:<:]]',Emp.id,'[[:>:]]')
GROUP BY Emp.name;
See it in action here.
UPDATE: added the IF(Played_by IS NULL,0,COUNT(*)) instead of COUNT(*). This means that if an employee doesn't play anything they'll have a 0 as their Num_Sports. See it here (I also added in those ^ characters and it still works.
What it does is joins the Emp table to the Sports table if it can find the Emp.id in the corresponding Played_by column.
For example, if we wanted to see what sports Ajay played (id=1), we could do:
SELECT *
FROM Emp, Sports
WHERE Sports.Played_by LIKE '%1%'
AND Emp.id=1;
The query I gave as my solution is basically the query above, with a GROUP BY Emp.name to perform it for each employee.
The one modification is the use of RLIKE instead of LIKE.
I use RLIKE '[[:<:]]employeeid[[:>:]]' instead of LIKE '%employeeid%. The [[:<:]] symbols just mean "make sure the employeeid you match is a whole word".
This prevents (e.g.) Emp.id 1 matching the 1 in the Played_by of 3,4,11,2.
You do not want to store your relationships in a column like that. Create this table:
CREATE TABLE player_sports (player_id INTEGER NOT NULL, sport_id INTEGER NOT NULL, PRIMARY KEY(player_id, sport_id));
This assumes you have an id column in your sports table. So now a player will have one record in player_sports for each sport they play.
Your final query will be:
SELECT p.name, COUNT(ps.player_id)
FROM players p, player_sports ps
WHERE ps.player_id = p.id
GROUP BY p.name;