Query to Pull Data from 2 Junction Tables - mysql

I'm creating a database for comics. Right now I have 3 main tables (comics, publishers, people) and 3 junction tables (person2comic, publisher2comic and person2publisher). I want to be able to have a search form that allows searching by any combination of title, issue number, publisher, and person. When referencing only one junction table, I use a variation of this depending on what's being searched for:
SELECT comicTitle, comicIssue, firstName, lastName
FROM person2comic
JOIN comics ON comics.comicID = person2comic.comicID
AND comics.comictitle LIKE "%walk%" ;
If someone were to search by title, publisher and person, I'm not sure how to set up the statement since it would require using two of the junction tables. Would it be a nested query situation or something else?

You can have arbitrarily many joins. Not exactly sure on all of your column names, but this should roughly work:
SELECT *
FROM people
JOIN person2comic p2c ON people.id = ptc.person
JOIN comic ON p2c.comic = comic.id
JOIN publisher2comic pub2c ON comic.id = pub2c.comic
JOIN publisher ON pub2c.publisher = publisher.id
Also note that your schema may be inefficient if you relationships all aren't many-to-many. See my comment.

Related

count students from table where join mysql

I have the databaase in icon below. I want
to count all students from Subject with name Psychology and class with name Class5.
the percentage of students with status "Something" from subject with name Psychology and class with name Class5.
All students and the class name from Class "Class6" that are male.
I've tried for example
(in english:)
SELECT COUNT(student_name) AS NumberOfStudents FROM student_srms JOIN class_srms JOIN subject_srms WHERE class_srms.class_name='Class5' AND subject_srms.subject_name='Psychology'
But returns NumberOfStudents = 20, but 20 are all student entries.
The issue likely stems from your FROM clause. It's not enough to just say JOIN. You need to specify the relationship of the columns between the two tables being joined with an ON clause:
FROM student_srms
JOIN class_srms
ON student_srms.student_id = class_srms.student_id
JOIN subject_srms
ON class_srms.subject_id = subject_srms.subject_id
I believe in MySQL there is a NATURAL JOIN which will tell mysql without an ON clause to just join on column names that are similar between the two tables, but that feels dirty to me and could cause failures later on in an applications lifecycle if new columns are introduced to tables that share names, but not relationships, so I would just steer clear of that.
I have a suspicion that your diagram showing tables/columns is incorrect based on the error you are reporting in the comments. Instead, try (and I'm totally guessing blind here at this point):
FROM student_srms
JOIN student_class
On student_srms.student_id = student_class.class_id
JOIN class_srms
ON student_class.class_id = class_srms.student_id
JOIN subject_srms
ON class_srms.subject_id = subject_srms.subject_id
That adds in that student_class relationship table so you can make the jump from student to class tables. Fingers crossed.

Dealing with polymorphic association: is there such a thing as "too many" LEFT JOIN?

I have ten tables (Product_A, Product_B, Product_C, etc.), each of them having a primary key pointing to a row in a the parent table Product.
Basically, I have applied the recommendations coming from the SQL antipattern book from Bill Karwin (this antipattern solution described here:
https://fr.slideshare.net/billkarwin/practical-object-oriented-models-in-sql/34-Polymorphic_Assocations_Exclusive_Arcs_Referential )
In order to load a child product, I use something like this:
SELECT * FROM Product
LEFT JOIN Product_A USING (product_id)
LEFT JOIN Product_B USING (product_id)
LEFT JOIN Product_C USING (product_id)
LEFT JOIN Product_D USING (product_id)
WHERE product_id = 1337
etc.
I fear that the more types of child table products I get, the more JOIN clause I will have to add, causing the query to end up incredibly slow.
Is using LEFT JOIN in order to prevent polymorphic associations antipattern still a solution if we work on tens of sub child tables?
Should I start thinking of up using a query on parent table Product in order to grab a "product_type" and then execute another query on the appropriate child table depending on the value stored in the "product_type" column in the parent table?
Update: first replies on this topic state that this is bad design and that I should create a single table combining the colums from the child tables. But each product type has its own attributes. To say it otherwise: "A TV might have a pixel count, but that wouldn't make much sense for a blender." #TomH
Thank you
MySQL has a hard limit on the number of joins. The limit is 61 joins, and it's not configurable (I looked at the source code and it's really just hard-coded). So if you have more than 62 product types, this is not going to work in a single query.
If the data were stored in the structure you describe, I would run a separate query per product type, so you don't make too many joins.
Or do a query against the Product table first, and then additional queries to the product-type specific tables if you need details.
For example, when would you need to gather the product-specific details all at once? On some kind of search page? Do you think you could design your code to show only the attributes from your primary Product table on the search page?
Then only if a user clicks on a specific product, you'd go to a different page to display detailed information. Or if not a different page, maybe it'd be a dynamic HTML thing where you expand a "+" button to fetch detailed info, and each time you do that, run an AJAX request for the details.
Yes, you can use the product_type (so called "discriminator") to help the DBMS produce a better query plan and avoid unnecessary joins. You can do something like this:
SELECT
*
FROM
Product
LEFT JOIN Product_A
ON product_type = 1 -- Or whatever is the actual value in your case.
AND Product.product_id = Product_A.product_id
LEFT JOIN Product_B
ON product_type = 2
AND Product.product_id = Product_B.product_id
LEFT JOIN Product_C
ON product_type = 3
AND Product.product_id = Product_C.product_id
LEFT JOIN Product_D
ON product_type = 4
AND Product.product_id = Product_D.product_id
WHERE
Product.product_id = 1337
The DBMS should be able to short-circuit all "branches" that don't have the right product_type and avoid the corresponding joins.1
Whether this is actually better than using a separate query to fetch product_type and then choosing the corresponding "special" query (and incurring another database round-trip) - that's something you should test. As always, test on the representative amounts of data!
1 At least Oracle or SQL Server would do that - please check for MySQL!
What kind of data is going into these tables? Is it simply metadata about the products? If that's the case you could create a tall table that describes each product.
For example, a Product_Details table that has three columns: product_id, product_data_key, value. Where product_data_key is what used to be the columns in Product_A, Product_B, Product_C...
You could even have a separate table that better describes product_data_key so it's just a foreign key in Product_Details.
Maybe change your design? One product can have many attributes (and many of the same attributes), and those attributes have values.
I suggest three tables:
Products ProductsAttributes Attributes
-Product_Id -Product_Id -Attribute_Id
-... -Attribute_Id -Attribute_Name
-Value -...
-...
Using as such:
SELECT p.Product_Id, a.Attribute_Name, pa.Value FROM Products p
JOIN ProductsAttributes pa ON pa.Product_Id = p.ProductId
JOIN Attributes a ON a.Attribute_Id = pa.Attribute_Id
Then you can reuse attributes, tie them to products, and store their values. Each product only has the attributes that it needs.

Using returned SELECT info in mySQL

In a MySQL database I have two tables linked in a join. One table contains people details and another book details. I want to search the databases for a particular author (principalWriter) and return all the co-authers (additionalWriters) they have worked with.
So I use
SELECT
books.additionalWriters,
people.name
FROM
books
INNER JOIN people ON books.principalWriter = people.personID
WHERE personID = 1;
And this returns each book the author has worked on with the additional writers ID.
However how can I then use these returned IDs to look up their respective names in the name table? Is there a single query I can do to accomplish this?
The problem here is not the query itself but rather the database design. You should have this tables:
Writers(*ID*, name): Will store all writers (principal or not)
Books(*ID*, name): Will store all books
Writers_Books(*WriterID*, *BookID*, Principal): This will store the relationship between the writers and the books and will specify if the writer for that book is principal or not
Primary keys are surrounded by asterisks
Note: You could also remove the Principal field and add it to the Books table, but if a book happens to have to principal writers, you won't be able to solve that with that schema.
Once you update your design, the query will be much easier.
you need to join it again to peoples table. try this:
SELECT a.AdditionalWriters, c.name, b.name
FROM books a INNER JOIN people b ON
a.PrincipalWriter = b.personID
INNER JOIN people c ON a.additionalWriters = c.PersonID
WHERE b.PersonID = 1
Try the following:
SELECT people.name FROM people WHERE personID in
(SELECT
books.additionalWriters,
FROM
books
INNER JOIN people ON books.principalWriter = people.personID
WHERE personID = 1);

Please explain MySQL Joins in simple language

Please explain to me joins in simple language. Please do not post a web link as I need to read how a developer understand it, not an author.
Best I can point you to is A Visual Explanation of SQL Joins.
The diagrams helped me a lot.
Adding the main diagrams from the linked post here.
Inner Join
Inner join produces only the set of records that match in both Table A and Table B.
Full outer join
Full outer join produces the set of all records in Table A and Table B, with matching records from both sides where available. If there is no match, the missing side will contain null.
Left outer join
Left outer join produces a complete set of records from Table A, with the matching records (where available) in Table B. If there is no match, the right side will contain null.
Given Table Person And Information
SELECT *
FROM Person INNER JOIN
Information ON Person.ID = Information.ID
Will only return rows from both tables where both tables have the same IDs. So only if the ID exists in both Person and Information will the row be returned.
SELECT *
FROM Person LEFT JOIN
Information ON Person.ID = Information.ID
Will return all rows from Person, and only those that match from Information, where it does not match, it will return NULLs
SELECT *
FROM Person LEFT JOIN
Information ON Person.ID = Information.ID
WHERE Information.ID IS NULL
Will return all rows from Person that DOES NOT HAVE an entry in Information. This shows you the list of persons that do not have their Informaton updated yet.
I'm interpreting your question to mean joins in a very general sense, not each type of join, so if this is off the mark then I apologize:
Basically, joins enable you to get data from multiple tables in a single query by adding columns to your result set. So if you had the following tables:
Books (ID, Title, AuthorID)
Authors (ID, Name)
You could get a result set that looked like this:
Book | Author
'The Sirens of Titan' | 'Kurt Vonnegut'
'The Old Man and the Sea' | 'Earnest Hemingway'
By joining the two tables together like so:
select Books.Title as Book, Authors.Name as Author
from Books
inner join Authors on Authors.ID = Books.AuthorID
An inner join is the simplest type of join; it may be difficult to understand the point of outer joins without first having a strong grasp of inner joins and their uses.

MySQL -- joining then joining then joining again

MySQL setup: step by step.
programs -> linked to --> speakers (by program_id)
At this point, it's easy for me to query all the data:
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
Nice and easy.
The trick for me is this. My speakers table is also linked to a third table, "books." So in the "speakers" table, I have "book_id" and in the "books" table, the book_id is linked to a name.
I've tried this (including a WHERE you'll notice):
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
LIMIT 5
No results.
My questions:
What am I doing wrong?
What's the most efficient way to make this query?
Basically, I want to get back all the programs data and the books data, but instead of the book_id, I need it to come back as the book name (from the 3rd table).
Thanks in advance for your help.
UPDATE:
(rather than opening a brand new question)
The left join worked for me. However, I have a new problem. Multiple books can be assigned to a single speaker.
Using the left join, returns two rows!! What do I need to add to return only a single row, but separate the two books.
is there any chance that the books table doesn't have any matching columns for speakers.book_id?
Try using a left join which will still return the program/speaker combinations, even if there are no matches in books.
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
LEFT JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
LIMIT 5
Btw, could you post the table schemas for all tables involved, and exactly what output (or reasonable representation) you'd expect to get?
Edit: Response to op author comment
you can use group by and group_concat to put all the books on one row.
e.g.
SELECT speakers.speaker_id,
speakers.speaker_name,
programs.program_id,
programs.program_name,
group_concat(books.book_name)
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
LEFT JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
GROUP BY speakers.id
LIMIT 5
Note: since I don't know the exact column names, these may be off
That's typically efficient. There is some kind of assumption you are making that isn't true. Do your speakers have books assigned? If they don't that last JOIN should be a LEFT JOIN.
This kind of query is typically pretty efficient, since you almost certainly have primary keys as indexes. The main issue would be whether your indexes are covering (which is more likely to occur if you don't use SELECT *, but instead select only the columns you need).