I have a database having the following structure. Each book can have multiple authors, and each author can write multiple books.
[book:book_id, book_name, book_price]
[author: author_id, author_name]
[link:book_id, author_id]
{book_id's and author_id's are linked. the complete structure is shown here: http://i.stack.imgur.com/vIFNU.png}
Now each book has a price (currency). 30% of the price should be equally distributed to each author who have contributed to the book.
My question is how to find the total payment for each author for a particular year.
[I thought of a solution my self. I could do only up to step 1. If you can provide me some hints or materials where I can find how do such manipulations, it would be very helpful]
Algorithm of my solution is:
So for each book_id, I need to find the number of author_id's in the middle table who has the same book_id. (could do it by query)
If I divide the book_price by number_of_author_in_book and multiply it with 30/100, I get the money for that book that will go to the each author's account for that book (say payment_of_one_author_in_book)
For each author_id in the middle table, I look up for the corresponding book_id and add the payment_of_one_author_in_book for that author_id to a new variable (author_payment_this_year) corresponding to the author ID, if the year matches to the query year.
Thanks in advance
This example includes aliases and subqueries.
SELECT
a.author_id,
a.author_name,
SUM(share.auth_share) AS author_total
FROM (link l
INNER JOIN (
SELECT
b.book_id,
( [b.book_price] * 0.3 ) / [no_auth] AS auth_share
FROM book b
INNER JOIN (
SELECT
l.book_id,
COUNT(l.author_id) AS no_auth
FROM link l
GROUP BY l.book_id) AS ac
ON b.book_id = ac.book_id) AS share
ON l.book_id = share.book_id)
INNER JOIN author a
ON l.author_id = a.author_id
GROUP BY a.author_id,a.author_name
SELECT author.AuthorId, author.Author_name, book.Book_Name, book.Book_Price, [Book_Price]/DCount("[Author_id]","[Link]","[Book_id]=" & [Book_Id]) AS Share
FROM (author INNER JOIN link AS link_1 ON author.AuthorId = link_1.Author_id) INNER JOIN book ON link_1.Book_id = book.Book_Id;
Basically, it is just a join of the three tables. The only tricky bit is using DCount function to add up how many authors in Link share this book_ID
Related
I am haveing a lot of trouble trying to work out this question
Write a query to show the number of authors who have written a book
Author(AuthorID, AuthorName, Address, TelephoneNo, PublisherCode)
Book (BookID, Name, ReleaseDate, Price, AuthorID)
I have
SELECT a.AuthorName, COUNT(b.*) AS ‘number of books written’
FROM Author a JOIN Book b ON a.AuthorID = b.BookID
GROUP BY a.AuthorName;
Which counts the number of books each author has written.
This is not the correct I know, but I can not figure it out??
Assuming the requirement is to count authors that have at least one book, the simplest query to satisfy that would be:
SELECT COUNT(DISTINCT b.authorid)
FROM book b
We probably want to assign an alias (name) to the returned column:
SELECT COUNT(DISTINCT b.authorid) AS `count_of_authors_who_have_at_least_one_book`
FROM book b
We could also do a join to the author table, but that isn't necessary here, unless there are values of authorid in the book table that don't appear in the author table (i.e. there's not a foreign key constraint, or referential integrity is not enforced)
Queries to get authors that have two or more books would be a bit more complicated:
SELECT COUNT(*)
FROM ( -- authors of two or more books
SELECT b.authorid
FROM book b
GROUP
BY b.authorid
HAVING COUNT(1) >= 2
) c
If we want authors that have EXACTLY one book (not two or more) we can tweak the condition in the HAVING clause:
SELECT COUNT(*) AS `count_authors_of_exactly_one_book`
FROM ( -- authors of exactly one book
SELECT b.authorid
FROM book b
GROUP
BY b.authorid
HAVING COUNT(1) = 1
) c
You were pretty close. You need to join on the author ID. You are currently mixing the author and book ID's, which won't match correctly.
SELECT
a.AuthorName,
COUNT(b.*) AS ‘number of books written’
FROM Author a
JOIN Book b ON a.AuthorID = b.AuthorID
GROUP BY a.AuthorName;
If you wanna get just a number that indicate total count of Author that wrote at least on book use below query
select count(*) as author_count from Author where exists (select 1 from Book where Book.AuthorID = Author.AuthorID)
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)
This question already has answers here:
SQL query return data from multiple tables
(6 answers)
Closed 6 years ago.
I have a problem here. I created a database with different tables. I have a table named "movie" with 108 movies. This table includes columns
"id, name, director, year, country",
table named "reviews" which includes columns
"movie_id" (movie_id links to the id of movie which was reviewed),
"review_score" (1 to 5 points),
"review (with review text)",
"critics_id" (which links to the critics from critics-table),
and also table named "critics" which icludes columns
"critics_name" and "critics_id".
The problem is that there is over 100 movies but only 10 of them are reviewed and I have to list only reviewed movies but reviews and movies are in different tables. When I try simple command SELECT name, review FROM movie, reviews - I get a huge list with repeating movies (see on picture) my table
Is there any command which could list only reviewed movies and so that every reviewed movie would be in the list only once?
Thanks beforehand!
This is where you use a join. You'd select from the movie table and join the reviews table, then filter by movies that have a joined review.
For example:
SELECT movie.*, review.review_score, review.review
FROM movie
LEFT JOIN reviews AS review
ON review.movie_id = movie.movie_id -- assuming this column name is correct...
WHERE review.movie_id IS NOT NULL
You could also add another join to your critics table:
SELECT movie.*, review.review_score, review.review, critic.critics_name
FROM ....
LEFT JOIN critics AS critic
ON critic.critics_id = review.critics_id
WHERE critic.critics_id IS NOT NULL
Please note that this example uses left join to highlight the way that the join works, in that if it doesn't find a match then the joined result will be null.
If you used an inner join instead, you wouldn't get any results that don't match the on clause, and as such you wouldn't need the WHERE id IS NOT NULL parts at all.
An inner joined example of the above would return movies that are reviewed, as well as the critic's name:
SELECT movie.*, review.review_score, review.review, critic.critics_name
FROM movie
INNER JOIN reviews AS review
ON review.movie_id = movie.id
INNER JOIN critics AS critic
ON critic.critics_id = review.critics_id
Also - I'm using AS to alias the plural table names to a singular name purely for semantic reasons. You could continue to use reviews and critics if you wanted to without the AS aliases.
Hope below query helps you:
SELECT
m.name,
c.critics_name,
r.review_score,
r.review_text
FROM movies m
JOIN reviews r ON r.movie_id = m.id
JOIN critics c ON c.critics_id = r.critics_id
for distinct movies change query as below:
SELECT DISTINCT m.name
FROM movies m
JOIN reviews r ON r.movie_id = m.id
JOIN critics c ON c.critics_id = r.critics_id
I have 3 tables in a MySQL database,
Author
Book
Author_has_Book.
The author has two columns
idauthor
name
Book also has two columns
idbook
name
Author_has_Book also has two columns
foreign keys of an author and a book book_idbook
author_idauthor.
I have successfully inserted an author and a book into both tables and I have entered their keys into the join table.
Now how do I use this join table to get all books by a certain author, or all authors for a book? Is this accomplished with joins?
edit: The suggested duplicate is not the same question.
It will get you all records
SELECT Author.*
FROM Author Author
INNER JOIN Author_has_Book AuthorHasBook
ON Author.idauthor = AuthorHasBook.author_idauthor
INNER JOIN Book Book
ON AuthorHasBook.book_idbook = Book.idbook
and if you wish to select any specific author, you just need to mention it in where clause, see example below:
SELECT Author.*
FROM Author Author
INNER JOIN Author_has_Book AuthorHasBook
ON Author.idauthor = AuthorHasBook.author_idauthor
INNER JOIN Book Book
ON AuthorHasBook.book_idbook = Book.idbook
WHERE Author.idauthor = 'your author id'
Try this query
select a.name,c.name from book a
join Author_has_Book b on a.idbook=b.idbook
join author c on b.idauthor=c.idauthor
i have a database with 3 tables in a many-to-many relationship. one is the books, the other is the authors and the third is their junction (used to join the two). the database is MySQL
a book can be made by many authors, and authors make many books.
now i want to get the books (with authors) like say 8 books at a time. i made this query:
//first table
SELECT * FROM `books`
//join the junction
LEFT JOIN books_authors ON books.book_id = books_authors.book_id
//join the authors
LEFT JOIN authors ON books_authors.author_id = authors.author_id
//limit to 8, start at S
limit S, 8
works fine when one-to-one. but when a book has more authors, like say 3 each, the sql result will have 8 x 3 rows in total (due to the 2D nature of the result) for all the details. but the query still clips it to 8 - i don't get all the details.
how to get 8 books with all details?
You could limit the number of books in a subquery:
select *
from (
select *
from books
limit 6, 7
) b
left join
ba ba
on b.book_id = ba.book_id
left join
authors a
on ba.author_id = a.author_id
Do not know what SQL features does your server support, but there are windowing functions that are supported by many. Essentially, you compute a sequential number to every book by given author in a particular ordering, and select only these having that number less or equal than 8 (for some value of 8). The numbering is provided by the function ROW_NUMBER():
SELECT * FROM (
SELECT a.author_id, b.book_id, a.name, b.title,
ROW_NUMBER() OVER (PARTITION BY a.author_id ORDER BY b.title) book_seq
FROM author a
LEFT JOIN book b on a.author_id = b.author_id) dummy
WHERE book_seq <= 8
Above, ORDER BY b.title defines the ordering in which you select the 8 minimal book records.
EDIT: mySQL does not support ROW_NUMBER(), so my answer does not apply as it is. There is an interesting article with examples on simulating ROW_NUMBER with OVER (PARTITION) in mySQL.
Of the top of my head, I'd guess you could try something like
select *
from ( select * from books limit s, 8 ) booksLimited
join books_authors on booksLimited.book_id = books_authors.book_id
join authors on books_authors.author_id = authors.book_id
But I'm not going to go to the effort of installing mySQL just to try this out for you. If it doesn't work, comment on it and I'll delete the answer.