im taking my very first programming (sql) class. I've got no tech background whatsoever and I'm having a little trouble getting the code down.
here is the what the database looks like.
BOOK
BOOK_CODE (UNIQUE)
BOOKTITLE
PUBLISHER_CODE
BOOKTYPE
PRICE
INVENTORY
BOOK_CODE (UNIQUE)
BRANCH_NUM (UNIQUE)
ON_HAND
The question is, list the books (titles) that are available in branches 3 and 4 (on both at the same time).
Im thinking i need to use the following tables: booktitle on the book table , bookcode on both tables ( book and inventory), and branch_num on inventory table.
also the answer can only show the book titles available on branches 3 and 4 (at the same time) and no other columns.
sorry if im making no sense. like i said im a n00b.
select distinct BOOKTITLE from BOOK a, INVENTORY b
where a.BOOK_CODE = b.BOOK_CODE and a.BOOK_CODE in
(select distinct p.BOOK_CODE from Inventory p, Inventory q where p.BOOK_CODE =
q.BOOK_CODE
and p.BRANCH_NUM = 3 AND q.BRANCH_NUM = 4);
SELECT BOOKTITLE FROM BOOK
WHERE BOOK_CODE IN (SELECT BOOK_CODE FROM INVENTORY WHERE BRANCH_NUM = 3 AND ON_HAND > 0)
AND BOOK_CODE IN (SELECT BOOK_CODE FROM INVENTORY WHERE BRANCH_NUM = 4 AND ON_HAND > 0);
OR
SELECT BOOKTITLE FROM
(
SELECT BOOK.BOOK_CODE, BOOKTITLE, COUNT(*) AS BRANCH_COUNT FROM BOOK
INNER JOIN INVENTORY ON BOOK.BOOK_CODE = INVENTORY.BOOK_CODE
AND INVENTORY.BRANCH_NUM IN (3, 4)
GROUP BY BOOK.BOOK_CODE, BOOKTITLE
) B
WHERE B.BRANCH_COUNT = 2;
Please try this:
SELECT BK.BOOKTITLE
FROM BOOK BK
INNER JOIN INVENTORY INV1
ON INV1.BOOK_CODE = BK.BOOK_CODE
INNER JOIN INVENTORY INV2
ON INV2.BOOK_CODE = BK.BOOK_CODE
WHERE INV1.BRANCH_NUM = 3
AND INV2.BRANCH_NUM = 4
Your question tells you the fields you need from the table, which gives you this as a starting point:
SELECT booktitle FROM book . . .
That alone will give you a list of every booktitle in the table book, but you want to filter it down to only those books in both branch_num 3 and 4.
When you JOIN two or more tables, you're looking to match rows based on some shared valued; book_code, in this case.
Since you want to see only those books in both tables, you'll want to use an INNER JOIN. (I'm assuming ON_HAND is an INT describing the number of copies in the branch.)
SELECT booktitle FROM book
INNER JOIN inventory USING (book_code)
WHERE inventory.onhand > 0
That query will return a list of every book that is available in any branch. Modifying the query, you can restrict it only to those books in branch_num 3:
SELECT booktitle FROM book
INNER JOIN inventory USING (book_code)
WHERE inventory.onhand > 0 AND inventory.branch_num = 3
...but you need books in both branches 3 and 4, so you'll need to do another join on the inventory table. But how can you distinguish the two? Use table aliases:
SELECT booktitle FROM book
INNER JOIN inventory AS inventory1 USING (book_code)
INNER JOIN inventory AS inventory2 USING (book_code)
WHERE inventory1.onhand > 0 AND inventory1.branch_num = 3 AND inventory2.onhand > 0 AND inventory2.branch_num = 4
That should give you what you're looking for.
For a much better explanation of joins, see MySQL Joins and A Visual Explanation of SQL Joins.
[Note: this problem could also be using subqueries, which you should try to do.]
Related
I can't seem to figure out what join I need to do. I have 3 tables which have all the information. I've map out the steps but I'm unsure what to do now, I need to display all the books listed from a specific author.
There is 3 tables I need to use
Author - which contains author_code, fname & lname.
Book - which contains book_code, title, publisher_code, type, price and paperback
Wrote - which contains book_code, author_number and sequence
Here's the code so far, really I know I should use author name but i can't figure it out.
SELECT BOOK_CODE
FROM WROTE
WHERE AUTHOR_NUM = 20
LEFT JOIN AUTHOR
ON WROTE.AUTHOR_NUM = AUTHOR.AUTHOR_NUM;
You need list of book, so start with book table then join it with wrote to get specific author and then join to author to get author data
SELECT BOOK_CODE
FROM BOOK
INNER JOIN WROTE
ON WROTE.BOOK_CODE = BOOK.BOOK_CODE
AND WROTE.AUTHOR_NUM = 20
INNER JOIN AUTHOR
ON AUTHOR.AUTHOR_NUM = WROTE.AUTHOR_NUM
SELECT Book.book_code
FROM Book
LEFT JOIN WROTE ON WROTE.book_code = Book.book_code
LEFT JOIN AUTHOR ON AUTHOR.author_code = WROTE.author_number
WHERE AUTHOR.author_code IN (123,456)
I am having a major problem joining 5 tables because each table only has 1 column in common with only 1 other table.
Here are my tables and columns in each table:
TABLE (COLUMNS)
person (person_id, first_name, last name)
building (building_id, building_name)
room (room_id, room_number, building_id, capacity)
meeting (meeting_id, room_id, meeting_start, meeting_end)
person_meeting (person_id, meeting_id)
OK, now here is what I am trying to do (pasted from a homework assignment):
Construct the SQL statement to find all the meetings that person_id #1 has to attend. Display the following columns:
Person’s first name
Person’s last name
Building name
Room number
Meeting start date and time
Meeting end date and time
Now I know how to join 2 tables but I have no idea how to pull info from 5 different tables like this.
I tried looking up how to do this and it just says to do a UNION command, and I am just learning and have yet to cover that.
As UNION is used to combine the result from multiple SELECT statements into a single result set, you don't need it for this scenario. You have to join all the tables one by one based on their Id.
SELECT P.First_Name, P.Last_Name, B.Building_name, R.Room_Number,
M.Meeting_Start, M.Meeting_End FROM Person P
JOIN Person_Meeting PM ON P.Person_Id = PM.Person_Id
JOIN Meeting M ON PM.Meeting_Id = M.Meeting_Id
JOIN Room R ON M.Room_Id = R.Room_Id
JOIN Building B ON R.Building_Id = B.Building_Id
WHERE P.Person_Id = 1
I have three tables and I need to join as follows.
INVENTORY
BOOK_CODE, BRANCH_NUM, ON_HAND
BOOK
BOOK_CODE, TITLE
AUTHOR
AUTHOR_NUM, AUTHOR_LAST
Basically, I need to select the author name, book title, and on-hand count for all books from Branch 4 (in inventory). The problem is, the AUTHOR table has to be reached through a fourth table.
WROTE
BOOK_CODE, AUTHOR_NUM
If all the tables had that BOOK_CODE, I could do it easy, but I can't figure out how to jam it all into one query. I've tried the following:
SELECT TITLE, AUTHOR_LAST, ON_HAND
FROM BOOK, AUTHOR, INVENTORY
WHERE BOOK.BOOK_CODE = INVENTORY.BOOK_CODE
AND AUTHOR_NUM IN
(SELECT AUTHOR_NUM
FROM WROTE
WHERE WROTE.BOOK_CODE = INVENTORY.BOOK_CODE)
AND BRANCH_NUM='4';
But this returns the wrong fields, so I don't know what I'm doing wrong.
You can "chain" your joins:
SELECT title, author_last, on_hand
FROM book b
JOIN inventory i ON b.book_code = i.book_code
JOIN wrote w ON b.book_code = w.book_code
JOIN author a ON a.author_num = w.author_num
WHERE branch_num = 4
You should try Joining it will be Helpful rather that Sub query
SELECT a.AUTHOR_LAST,d.TITLE,c.ON_HAND FROM WROTE a INNER JOIN AUTHOR b
ON a.AUTHOR_NUM=b.AUTHOR_NUM INNER JOIN INVENTORY c
ON c.BOOK_CODE=a.BOOK_CODE INNER JOIN BOOK d
ON a.BOOK_CODE=d.BOOK_CODE
WHERE c.BRANCH_NUM=4
Try like this
I have two tables, one is books, the other is editions. Each edition has an attribute like "hardback" or "paperback" in a column "edtype". There can be multiple edition rows related to each book row.
select books.id, books.name, editions.id, editions.edtype from books
join editions on books.id=editions.bookid;
would return something like:
1 name1 1 hardback
1 name1 2 paperback
2 name2 3 paperback
3 name3 4 hardback
What I want to be able to do is find books that have (1) both a hardback and paperback edition:
1 name1
(2) A paperback but not a hardback edition
2 name2
(3) A hardback but not a paperback edition
3 name3
I have the following query, which I think does (1), but I'm not quite convinced (because the number of rows it returns appears too low):
select id, name
from books
left join editions e1 on ( books.id = e1.bookid and e1.edtype = 'paperback')
left join editions e2 on ( books.id = e2.bookid and e2.edtype = 'hardback')
where e1.edtype is not null
and e2.edtype is not null
group by b_id
;
While I'm looking at just two attributes right now, it would be nice to be able to expand that to multiple matching attributes in the editions table.
Any help gratefully received.
Let's start with your query, improved a bit to use table aliases and have distinct column names:
select b.id as book_id, b.name, e.id as edition_id, e.edtype
from books b join
editions e
on b.id = e.bookid;
You want to find books, so that suggests aggregation by books. Then we need to do something to understand the edtype. Try this:
select b.id as book_id, b.name,
(case when min(edtype) = max(edtype)
then min(edtype)
else 'Both'
end) as EdTypes
from books b join
editions e
on b.id = e.bookid;
group by b.id, b.name;
Following Gordon's code above I put in a couple of sub queries, and I think this does what I need (at least if I am only comparing two attributes):
select * from (
select books.name, bookid, (case when min(edtype) = max(edtype)
then min(edtype)
else 'Both'
end) as EdTypes
from
(select * from editions where (edtype = 'paperback' or edtype = 'hardback'))
as e2
join books on e2.bookid = books.id
group by bookid
) as titlelist
where edtypes = 'Both';
So, first select rows that are exclusively the ones you are interested in - so you have a min and max that makes sense. Then process with the case statement, join to books and eliminate duplicates. Then select each type in turn to get a count.
I have 2 tables structured like this:
People
----------
>ID, Name, Surname
Projects
----------
>ID, Assistant#1, Assistant#2, Assistant#3, Assistant#4,
> Assistant#5, Assistant#6, Assistant#7, Assistant#8, Assistant#9,
> Assistant#10, lot of other columns..... (every assistant column
> contains an integer, the people ID)
Now, I want to query my DB and get a single row from Project table (let's suppose row with ID=3) containing all columns of project table and in particular have each assistant replaced with it's correct name and surname.
I've managed to do this (with a lot of joins), but with my little sql experience my code looks very bulky and i'm sure that there's a better way to do this.
Thanks in advance.
SELECT p.Name, p.Surname
FROM People p
CROSS JOIN Project j ON (p.PeopleID = j.Assistant1 OR
p.PeopleID = j.Assistant2 OR
p.PeopleID = j.Assistant3 OR
.. etc)
AND j.ProjectID = 3
Your performance will be awful, but that's the price you pay for having a horribly designed database. The better solution is to map a project and people by the means of a decomposition:
CREATE TABLE proj_people
People_ID INT
Project_ID INT
Your structure is not clear to me. I advice you to create a clutch for projects.
Tables:
Peoples
people_id, people_name, people_surname
Projects
project_id, project_name
Clutch
project_id, people_id
Because you set a number of (possible) assistants at 10, that dictates that you are going to have to write code to accomodate those 10 assistants. This can be done a few ways: (have not tested)
select * from projects proj
left join people p1 on proj.assistant1 = p1.peopleid
left join people p2 on proj.assistant2 = p2.peopleid
left join people p3 on proj.assistant3 = p3.peopleid
left join people p4 on proj.assistant4 = p4.peopleid
left join people p5 on proj.assistant5 = p5.peopleid
left join people p6 on proj.assistant6 = p6.peopleid
left join people p7 on proj.assistant7 = p7.peopleid
left join people p8 on proj.assistant8 = p8.peopleid
left join people p9 on proj.assistant9 = p9.peopleid
left join people p10 on proj.assistant10 = p10.peopleid
otherwise you could do some trickery with
select proj.projectID,
(select * from people where peopleID = proj.assistant1),
(select * from people where peopleID = proj.assistant2),
(select * from people where peopleID = proj.assistant3),
(select * from people where peopleID = proj.assistant4),
(select * from people where peopleID = proj.assistant5),
(select * from people where peopleID = proj.assistant6),
(select * from people where peopleID = proj.assistant7),
(select * from people where peopleID = proj.assistant8),
(select * from people where peopleID = proj.assistant9),
(select * from people where peopleID = proj.assistant10)
from projects proj
If possible, it might be best to re-structure your datatables, and have a single assistant mapped to a single projectID:
PeopleID, Name, Surname
ProjectID, PeopleID
Therefore, you could do just one inner join and you will return a row for each assistant:
select * from projects proj
inner join people p
on p.peopleID = proj.peopleid
where proj.projectID = PROJECTID
Your problem is that you are trying to solve a data presentation issue at the database level and this is rarely a good idea. You should have a table holding projectid and assistantid only, having the combination of both columns as the primary key of the table. This will allow you to join this table to the assistant table and the project table only once and get all the results you need. This will also have the benefit of allowing you to support many more assistants on a project in the future without having to alter your queries to add yet more joins.
In conclusion you should have 3 tables:
Project
Assistant
ProjectAssistant (projectid,assistantid)
you want to restructure your tables. I hate seeing tables with multiple columns for essentially the same thing multiple times. If you restructure to have 3 tables:
Person
PeopleID | Name | Surname
Project
ProjectID | ...other columns except Assistant
Project_Person
ProjectID | PersonID
That way you can assign multiple assistants to the same project without the need for multiple Assistant columns. Then you could use something like this to get the required results:
SELECT proj.ProjectID, pers.Name, pers.Surname
FROM Person pers
INNER JOIN Project_Person pp ON pp.PersonID = pers.PersonID
INNER JOIN Project proj ON proj.ProjectID = pp.ProjectID
This will not return a single row, but that is not how SQL is designed to work.
If you don't want to/can't change the table structure then maybe use a user defined function?
CREATE FUNCTION GET_ASS_NAME (n1 ASS_NO)
RETURNS VARCHAR(50)
DETERMINISTIC
BEGIN
DECLARE fullname VARCHAR(50);
SELECT fullname = CONCAT(NAME, ' ', SURNAME) FROM PEOPLE WHERE ID = n1;
RETURN fullname;
END|
then
select GET_ASS_NAME(1), GET_ASS_NAME(2), GET_ASS_NAME(3), ...
from PROJECTS
where ID = 3
Perhaps a cleaner design is:
People table:
People ID, Name, Surname
ProjectAssistants table:
Project ID, Assistant Person ID
Projects table:
Project ID, lots of other columns (but no Assistant columns)
Then, the SQL becomes something like:
SELECT pr.*, p.*
FROM Projects pr
JOIN ProjectAssistants p_a ON pr.ProjectID = p_a ProjectID
JOIN People p ON p_a.AssistantPersonID = p.PeopleID
WHERE ProjectID = X
Not tested :)
Select * from project pr
left join people pe1 on pe1.peopleid = pr.astint1
left join people pe2 on pe2.peopleid = pr.astint2
left join people pe3 on pe3.peopleid = pr.astint3
left join people pe4 on pe4.peopleid = pr.astint4
left join people pe5 on pe5.peopleid = pr.astint5
left join people pe6 on pe6.peopleid = pr.astint6
left join people pe7 on pe7.peopleid = pr.astint7
left join people pe8 on pe8.peopleid = pr.astint8
left join people pe9 on pe9.peopleid = pr.astint9
left join people pe10 on pe10.peopleid = pr.astint10
where pr.projectid = 3