need help with mysql for book system - problem with where clause - sql

hello all im developing new project for publisher ...this project for develop system saved all books for that publisher ...............
we have 3 table
books
author
coauthor
problem case:
ieach book have 1 main author thats author could replay in alot of books and ieach book in some cases could have co-author this co author could be 1 or 2 or 3 or 12 note: this co author is already saved in author table
realtionship:
one to many between authors and books
many to many between coauthor and books and authors
table #1 authors table => table hold all authors
id - author_name
table #2 books table => table hold all books
id- title - author_id - publishing_year
table #3 co_authors table => hold all item which have alot of co author
item_id and authors_id
now how i can retrive all books for specific author if he is main author or co author

There're two ways you can go about this... One is to use a UNION, so something like:
SELECT id FROM books WHERE author_id = ?
UNION ALL
SELECT item_id FROM co_authors WHERE authors_id = ?
Another way you could solve this is to rework your database structure such that there is no author_id in the books table and instead you have a flag in co_authors called main_author or some such. Considering you may have books where it's unclear who the main author is, this may make more sense...

You can do something like this :
SELECT DISTINCT books.*, authors.*
FROM books
LEFT JOIN co_authors ON co_authors.item_id = books.id
INNER JOIN authors ON
authors.id = books.author_id OR
authors.id = co_authors.author_id
WHERE authors.id = ID
The where clause can also be written where authors.name = 'NAME' if you need to search by name and not id.
EDIT: added distinct to avoid multiple rows for the same book.

Related

sqlplus DELETE statement

I'm using sqlplus (oracle). I trying to write a statement to remove all authors that have not yet published a book based on the bookauthor table.
Since I have joined to tables , I don't know how to delete authors that have not yet published a book. Also I'm not sure If I got subquery correctly in order to get an output of authors who have not published a book.
DELETE
FROM bookauthor, books
WHERE authorid =
(SELECT authorid, count(*) pubdate
FROM bookauthor
JOIN books USING (isbn)
GROUP BY authorid
HAVING count(*) < 1);
This is what I have so far.
I can't get it run correctly.
For table books -> isbn, title, pubdate, pubid, cost, retail, discount, category
for table bookauthor -> isbn, authorid
There are several flaws in your delete statement:
You want to delete authors that haven't published a book. That means you should be looking for authors that have no entry in the bookauthor table. But you don't even mention any author table and DELETE FROM bookauthor, books instead, which makes no sense.
You compare WHERE authorid = (SELECT authorid, count(*) FROM .... How can one ID possibly equal a pair of ID and count? It would have to be WHERE authorid = (SELECT authorid FROM ... instead.
In your subquery you are asking for HAVING count(*) < 1. But every author that is in the table occurs at least once of course. So this condition is never met. You are kind of asking: "Which of the authors in the bookauthor table is not in the bookauthor table?".
As mentioned, all this only makes sense with an author table:
trying to write a statement to remove all authors that ...
You are showing two tables. The book table where each book is identified by its ISBN. And the bookauthor table that links books to authors. This is called a bridge table (or junction table or association table or ... &dash; well there are many names for this). It establishes an m:n relation between author and book, so one author can have many books and one book can be written by more than one author. (If you wanted an 1:n relation instead, so that a book can only be written by one author, then the author ID would be an attribute in the book table.) So there must be an author table you haven't shown that the bookauthor table is referring to.
It should be something like:
delete from author where authorid not in (select authorid from bookauthor);
DELETE
FROM bookauthor
WHERE authorid =
(SELECT authorid FROM bookauthor WHERE isbn NOT IN (SELECT isbn FROM books))
Try this.
DELETE
FROM bookauthor
WHERE authorid =
(SELECT authorid FROM bookauthor a LEFT OUTER JOIN books b ON a.isbn = b.isbn WHERE b.isbn IS null)
Or this.

Finding entities (in a many to many relationship) that are related to other entities

TL;DR problem
Entity A's have a many to many relationship with Entity B's. Given some Entity B's, how can I find all Entity A's that are related to those given Entity B's?
Actual Problem
I'm currently a bit stuck on a problem of SELECTing rows that are in a many to many relationship. The problem can be phrased like this:
Entity A: Book
Each book is stored in a table called Books that looks something like this:
DESCRIBE Books;
book_id | book_name | ... other metadata on books
-- book_id is a PRIMARY KEY
Entity B: Genres
A genre of some type, like Adventure or Romance, held in a table like this:
DESCRIBE Genres;
genre_id | genre_name
-- genre_id is a PRIMARY KEY
Relationship Holder: BookGenres
Each Entity A, Book, can be related to multiple Entity B, Genre. For example, Harry Potter can be related to the genre_names: 'Adventure' and 'Fantasy'. The relationships are stored here via. a (book_id, genre_id) tuple per row
DESCRIBE BookGenres;
book_id | genre_id
-- book_id REFERENCES Books(book_id)
-- genre_id REFERENCES Genres(genre_id)
-- (book_id, genre_id) is UNIQUE pairing
I want to be able to SELECT for books that have some desired genres. Let's say, I want to return all book_id where the book has the genres 'Adventure' and 'Fantasy'. The best I've been able to do is a self INNER JOIN and checking if the pairing ('Adventure', 'Fantasy') exists and returning that book_id, but this is terribly slow and if I'm searching for n genres relationships, I need n INNER JOINS! (unacceptable). My query looks something like this:
SELECT t1.book_id
FROM BookGenres as t1
INNER JOIN Genres as t2
ON t2.genre_id = t1.genre_id
INNER JOIN BookGenres as t3
ON t3.book_id = t1.book_id
INNER JOIN Genres as t4
ON t4.genre_id = t3.genre_id
WHERE t2.genre_name = 'Adventure' AND t4.genre_name = 'Fantasy'
To search for Books that have n specified relationships to desired Genres, I do n self INNER JOINS and check if such a pairing exists. This seems forbiddingly inefficient and I would like a bit of help on this query as I'm sure it is a common problem.
For starters, your relationship between Books and Genres is a Many to Many relationship, not a One to Many relationship.
As I understand your question, you want to return a list of all books that match all selected genres. Assuming you want additional information about the book, I have included the Books table
SELECT
Books.book_id
FROM
Books
WHERE
book_id IN (SELECT book_id
FROM BookGenres INNER JOIN Genres ON BookGenres.genre_id = Genres.genre_id
WHERE Genre.genre_name = 'Adventure') AND
book_id IN (SELECT book_id
FROM BookGenres INNER JOIN Genres ON BookGenres.genre_id = Genres.genre_id
WHERE Genre.genre_name = 'Fantasy')

Updating a table in oracle from another table with duplicate records present

I have two tables, lets say table books and table authors.
Table books has 4 fields, BookId, Title, Author, Publisher
Table authors has 5 field AuthorID, Title, Author, Publisher, Price
I want to update book to have the price field from authors. There is no one unique key so the criteria is if Title or Author or Publisher match then add that price to books. In my case authors has many duplicate values in Title, Author or Publisher so I get update errors because of duplicates. So basically how in oracle can I update books with that criteria and in case of duplicates just choose any record, it doesn't matter which and put it in there? Other languages have the top command that you can use to do such a thing but it looks like Oracle lack that.
I have tried various merge and update statements on my own with no luck. Something like:
UPDATE books n
set n.price = (
select t.price from authors t where
n.title= t.title or
n.author = t.author or
n.publisher = t.publisher)
But that statement isn't checking for duplicates and stopping them thus failing so I am missing that key.
The simple way is:
UPDATE books n
set n.price = (
select t.price from authors t where
( n.title= t.title or
n.author = t.author or
n.publisher = t.publisher )
and rownum = 1)

Counting positions in a row oracle

I'm making bookstore database and in the table "Authors" I've got column where i'd like to count books written by author in the table "Books". Table "Books" has foreign key "id_author". I have no idea how to do it, it's something like
SELECT COUNT(*) FROM Books WHERE id_author = "id of chosen author"
What to write in code in place of "id of chosen author"?
How to put it in a row in table "Author"?
You could join on the authors table and query by its columns (e.g., the first and last name):
SELECT COUNT(*)
FROM books b
JOIN author a ON b.id_author = a.id
WHERE a.firstname = 'John' AND a.lastname = 'Doe'
Depending on the application you have, the application might already have the author id. This means that you could just do a
SELECT COUNT(*) FROM Books WHERE id_author = "id of author provided by application"
Although the difference is probably not large compared to Mureinik's answer, this can enhance your performance on large databases as you do not have to do the join between author and book table.
Number of books by author:
select a.author, count(1)
from author a
join books b
on a.id = b.id_author
group by a.author

Issues with Group functions

I'm working on lab question that I just cannot come up with a solution, that would allow me to figure out the books that have more then one author(See the posted question below for my comments to make sense). My mind is totally blank on it. I'm very bad with word problems. I know that I have to do a JOIN statement which I've completed and I know that I have to use the COUNT function to count the number of authors but I honestly don't know how I would go about only counting the books that have 2 authors.
Any input would be appreciated. I tried to break it down into steps but it's just that one part that I'm not grasping in my mind.
Using the correct tables in your schema, create a query using either join operation you wish that will list the book title and number of authors for all books that have been written by more than one author. Give the title column an alias of "Book Title" and the column showing the number of authors an alias of "Number of Authors".
There is a BOOKS table and a AUTHOR table that are JOINED by a BOOK_AUTHOR table by their BOOKID in BOOKS and AUTHORID in AUTHOR.
I think I'm starting to understand that I have to use a mathematical equation to figure out more then one author. I don't understand the HAVING function all too well so I'm going to do more research on this one.
You were right, you need a JOIN, a COUNT, but also an HAVING to make sure there is more then one authors that written the book :
select title as 'Book Title', count(authors) as 'Number of Authors'
from books
join authors on books.id = authors.book_id
having count(authors) > 1
group by authors;
Make sure to adapt the table names and columns to the right one, as you didn't post them.
Note that if the books's author id column has the same name as the author id, you can use the USING keyword to join. then your query would become
select title as 'Book Title', count(authors) as 'Number of Authors'
from books
join authors on using(book_id)
having count(authors) > 1
group by authors;
Note that if you want to select only books that specifically have 2 authors you can change the having clause to having count(authors) = 2. But even if you ask for that in your question, according to the exercice you pasted, you did not understand properly the question.
I finally figured out the answer with all your help. Please see below:
SELECT DISTINCT Title "Book Title", COUNT(*)"Number of Authors"
FROM BOOKS JOIN
BOOK_AUTHOR ON
BOOKS.BookId = BOOK_AUTHOR.BookId
JOIN AUTHOR ON
BOOK_AUTHOR.AuthorId = Author.AuthorId
GROUP BY Title
HAVING COUNT(*) > 1;