MYSQL: Select Query with multiple values from one column - mysql

i am currently working with a MYSQL-Database whichhas three tables:
Books, Keywords and KeywordAssignment.
The tables Books and Keywords are in a many to many relationship therefore the table KeywordAssignment.
Now to my question: I want to search for a book with multiple (max: up to ten) keywords.
I've already tried a self join:
SELECT BookID
FROM Keywords K1 INNER JOIN
Keywords K2
ON K1.KeywordAssignmentID=K2.KeywordAssignmentID INNER JOIN
KeywordAssignment
ON KeywordAssignment.KeywordAssignmentID=K1.KeywordAssignmentID INNER JOIN
Books
ON KeywordAssignment.BookID=Books.BookID
WHERE K1.Keyword='Magic' AND K2.Keyword='Fantasy'
The problem is it only works if the given Keyword are in the right order. If they aren't there are more than one.
I appreciate your help thank you very much!

You need to GROUP BY BookID and a HAVING clause with the condition that both keywords are linked to that BookID:
SELECT b.BookID, b.Title
FROM Books b
INNER JOIN KeywordAssignment ka ON ka.BookID = b.BookID
INNER JOIN Keyword k ON k.KeywordID = ka.KeywordID
WHERE k.Keyword IN ('Magic', 'Fantasy')
GROUP BY b.BookID, b.Title
HAVING COUNT(DISTINCT k.Keyword) = 2
This code will return books that are linked to both 'Magic' and 'Fantasy'.
If you want either of the 2 keywords then remove the HAVING clause.

If I understand your question correctly, you want to query for books that have multiple key words. The key word there is have. I don't have MYSQL but the query should look something like this:
SELECT B.BookID, COUNT(*) as NumberOfKeywords FROM Books B
INNER JOIN KeywordAssignment KA
ON B.BookID = KA.BookID
INNER JOIN Keywords K
ON KA.KeywordID = K.KeywordID
GROUP BY B.BookID
HAVING NumberOfKeywords > 0 AND NumberOfKeywords <= 10
What we are doing is grouping by each book and then selecting the ones that have more than 0 keywords and less than 10.

Related

How to select all the authors from database with the number of books assigned to them?

I've the following DB structure:
Authors(id,name);
Books(id,title,authorId);
I want to select all fields from authors and the number of books they are assigned to. I've managed to get the result, but only for the authors that are assigned to at least one book, which is not what I want. I tried with the following query:
SELECT books.*,authors.*
FROM authors
FULL OUTER JOIN books
ON authors.id = books.authorId;
but it doesn't work.
I guess that you want a left join and aggregation:
select a.id, a.name, count(*)
from authors a
left join books b on b.authorId = a.id
group by a.id, a.name
outer join will bring back authors without books. Instead use inner join and your results will only bring back authors with at least 1 book.
I would recommend a correlated subquery:
SELECT a.*,
(SELECT COUNT(*)
FROM books b
WHERE a.id = b.authorId
) as num_books
FROM authors;
This allows you to use SELECT a.* from authors. If you put a GROUP BY in the outer query, you either need to list all the columns separately or be using a database that allows you to aggregate by a primary key, while selecting other columns (this is standard functionality but most databases do not support it).
Definitely you need LEFT JOIN and GROUP BY, but details is not clear enough from the task description. Let's try a kind of
SELECT b.*, ab.count
FROM authors AS a
LEFT JOIN (
SELECT authorId, COUNT(*) AS count
FROM books
GROUP BY authorId
) AS ab ON a.id = ab.authorId;
also, if you don't want to get NULL for some authors, you can apply such expression:
IFNULL(ab.count, 0) AS count

Join statement with multiple conditions

Ok I have two mysql table one called books and the other reading_tracking.
I have written the join query below:$query = "SELECT * FROM books inner join reading_tracking on book_num = book_id;
My dilemma is that i don't want all the from books i want a query which in theory would look something like this:
"SELECT * FROM books
where reading_status = 1 inner
join SELECT * FROM reading_tracking
on book_num = book_id";
If i try this as suggested:
SELECT * FROM books b
INNER JOIN reading_tracking rt
ON b.book_id = rt.book_num
WHERE rt.reading_status = 1;
The thing is in my program not all books in books table are in reading_tracking, user have to make an additional step, so i don't want books that are similar in both tables i want all the books from books where reading_status = 1; but want all the books from reading_tracking.
Your syntax should have the SELECT [x] FROM followed by the INNER JOIN, and then you need an ON to denote which field is synonymous between the two tables. In your example, I'm assuming you have a column book_id in books, and a column book_num in reading_tracking.
The WHERE clause should come after the ON, and additionally should specify which of the two tables you want to look for the column in.
This can be seen in the following:
SELECT * FROM books b
INNER JOIN reading_tracking rt
ON b.book_id = rt.book_num
WHERE rt.reading_status = 1;
This will search books for any row that has a reading_status of 1 in the reading_tracking table.
I assume column book_num is from books table, column book_id is from reading_tracking table. First join the 2 tables then use WHERE to filter the condition:
SELECT * FROM books b
INNER JOIN reading_tracking r
ON b.book_num = r.book_id
WHERE b.reading_status = 1;
You can use a subquery before doing the JOIN as shown below:
SELECT *
FROM (SELECT * FROM books WHERE reading_status=1) A
INNER JOIN reading_tracking B on B.book_num=A.book_id;
See Using MySQL Alias To Make The Queries More Readable

MySQL Join with looped reference query

I have a slightly complex table structure that I'm trying to query for a search function, but my queries keep timing out. Basically, it's a book search, and I'm focusing on the subject portion of that search.
The subjects table is simple (id and title), but there's a link table that refers it back to itself called subjects_subjects, which complicates things.
**subjects_subjects**
id (key)
subject_id (reference to subjects table)
see_subject_id (another reference to subjects table)
The reason for the looping reference is to catch subjects that don't contain any books, but point to subjects that do. For example, there's no books under the 'Travel' subject, so that subject has a link to 'Explorers' and 'Voyages' that do contain books. The point is to make searching easier.
So what I'm trying to do is allow the user to search for 'Travel', but return results from 'Explorers' and 'Voyages'. Here's my query that times out:
SELECT
BK.id,
BK.title
FROM
books BK
LEFT OUTER JOIN
books_subjects BS
ON BS.book_id = BK.id
WHERE
BS.subject_id IN (1639,3173)
OR BS.subject_id IN
(
SELECT
SS.see_subject_id
FROM
subjects_subjects SS
WHERE
SS.subject_id IN (1639,3173)
)
GROUP BY
BK.books_id
Extra info: There are 17000 books and over 3000 subjects in the database, with roughly 84000 book/subject references.
Can anyone help me figure out where am I going wrong here?
You're doing two things that MySQL optimizes poorly:
OR in the WHERE clause.
IN (SELECT ...)
Instead of OR, use two queries that you combine with OR. And instead of IN (SELECT ...) use a JOIN.
Also, you shouldn't use LEFT JOIN if you don't need to return rows from the first table with no matches in the second table, use INNER JOIN.
SELECT b.id, b.title
FROM books AS b
JOIN books_subjects AS bs ON bs.book_id = b.id
WHERE bs.subject_id IN (1639, 3173)
UNION
SELECT books AS b
JOIN books_subjects AS bs ON bs.book_id = b.id
JOIN subjects_subjects AS ss ON bs.subject_id = ss.see_subject_id
WHERE ss.subject_id IN (1639, 3173)

mysql query with multiple left join in 3 tables?

i have a search query which will retrieve information from 3 tables i made the query so it retrieve the information from 2 tables and i don't know if i can combine the third one or not
SELECT *
FROM articles
INNER JOIN terms
ON articles.ArticleID = terms.RelatedID AND terms.TermType = 'article'
the third query is
SELECT * FROM categories where CategoryID in (something)
something is a filed in the articles tables which have value like '3,5,8'
i want do this 2 queries into 1 query and i don't know if it can be done by 1 query or not
without looking at your schema (which would be helpful) and some sample data try this query
SELECT *
FROM categories,articles
INNER JOIN terms
ON (articles.ArticleID = terms.RelatedID AND terms.TermType = 'article')
WHERE
FIND_IN_SET(categories.CategoryID,articles.categories)
here is the definition for FIND_IN_SET()
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
If i understand you correctly. Looks like you have multiple categories for each article with the Category IDs all stored as a concatenated string.
SELECT A.*
FROM articles A
INNER JOIN terms T on A.ArticleID = T.RelatedID AND T.TermType = 'article'
LEFT JOIN categories C on C.CategoryID IN (3,5,8 OR A.CategoryIDs)
GROUP BY C.CategoryName
You want to LEFT JOIN since you may or may not have multiple categories, you can group by Categories to get disticnt category article pairs and CONCAT() to recombine article records as needed.

MySQL: get records from database and add a COUNT() column to the rows

I'm trying to retrieve books from one table and left join the chapters table. What I need from the second table is just the COUNT() of chapters available for those books and add that value as an extra column called chapters (or something else).
My current try looks like this:
SELECT b.*, count(c.chapter_nr) as chapters FROM books as b left join chapters as c on c.book_id = b.id
This only gets one from from the books table and adds the count() result to that row, but I'd like to get ALL the rows from the books table, hence the LEFT JOIN
SELECT b.*, count(c.chapter_nr) as chapters
FROM books as b
LEFT JOIN chapters as c on (c.book_id = b.id)
GROUP BY b.id
EXPLANATION
You need to group by the book in order to determine the actual chapter counts. If you were to leave out the GROUP BY clause, you would be retrieving a resultset of all chapters of every book. You simply want to limit the results to unique books and their corresponding chapter counts.
You are missing the GROUP BY clause:
SELECT b.*, count(c.chapter_nr) as chapters
FROM books AS b
LEFT JOIN chapters AS c ON c.book_id = b.id
GROUP BY b.id
Try :
SELECT b.*,
(select count(*) from chapters c where c.book_id = b.id) as chapters
FROM books b
This will return 0 if there are no chapters for a book.
Untested, but you need a "group by" clause to do what you want:
Select b.*, count(*) as chapters
from books b left outer join chapters c
on c.book_id = b.id
group by b.*