How to Query across multiple tables - sql

I have the following DB. Is this a proper query to get the first name of the authors loaned by Members ? or is there a more efficient way ?
select M.MemberNo, A.FirstName
from Person.Members as M
INNER JOIN Booking.Loans as L
on M.MemberNo = L.MemberNo
INNER JOIN Article.Copies as C
on L.ISBN = C.ISBN and L.CopyNo = C.CopyNo
INNER JOIN Article.Items as I
on C.ISBN = I.ISBN
INNER JOIN Article.Titles as T
on I.TitleID = T.TitleID
INNER JOIN Article.TitleAuthors as TA
on T.TitleID = TA.TitleID
INNER JOIN Article.Authors as A
on TA.AuthorID = A.AuthorID
;
go

The most direct way to get the names of authors of books that are lent out to members would be like this:
Get member loans (Booking.Loans)
Get books for member loans (Article.Items)
Get authors for the books for member loans (Article.TitleAuthors)
Get first names for authors for the books for member loans (Article.Authors)
select L.MemberNo, A.FirstName
FROM Booking.Loans as L
INNER JOIN Article.Items as I
on L.ISBN = I.ISBN
INNER JOIN Article.TitleAuthors as TA
on I.TitleID = TA.TitleID
INNER JOIN Article.Authors as A
on TA.AuthorID = A.AuthorID
The advantage to joining all the tables like you have your original query is to ensure that each intermediary table was INSERTed into or DELETEd from correctly.
For example, your original query wouldn't return any rows for a member if they had a loan (i.e. a row in the Loans table) but for some reason didn't have an entry in the Members table. If you can assume that (or don't care if) there will always be a row in the Members table if there is a row in the Loans table, then you can exclude the join to Person.Members.
You can apply this same logic to the other intermediary tables (Member, Copies, Title) too, if needed. If you need a specific piece of data from one of the these tables, though, then you would need to include them in the joins.

Related

Large Anti JOIN in SQL

I am working with a training database and I am trying to find the best way to create a list of all the users and all the classes they HAVEN'T taken. I have two tables a courses master table of 150 classes and a registration table that is 175K. I can create an anti join for a single person:
SELECT *
FROM tblCourses C --150 rows
Left JOIN tblTraineeCourses TC -- 175K
ON C.CourseId = TC.CourseId AND TraineeId = 2283539
WHERE TC.CourseID IS NULL
And an Anti Join to see what classes have never been taken
SELECT *
FROM tblCourses C --150 rows
Left JOIN tblTraineeCourses TC
ON C.CourseId = TC.CourseId --175K
WHERE TC.CourseID IS NULL
but i am looking for a way to produce a list table of every User ID and the classes they haven't taken.
I fully realize that this will produce a massive table but I need to do it for an analysis
Use a cross join to generate all combinations. Then filter out the ones that exist:
SELECT T.TraineeId, C.CourseId
FROM tblTrainees t CROSs JOIN
tblCourses C LEFT JOIN
tblTraineeCourses TC
ON TC.CourseId = C.CourseId AND
TC.TraineeId = T.TraineeId
WHERE TC.CourseID IS NULL;
This assumes that you have a list of trainees, in tblTrainees. You could replace that with:
(SELECt DISTINCT TraineeId FROM tblTraineeCourses
) t
If you don't have such a table.

Comparing two tables. Want to return a flag

My database has many tables with many relationships among them. But this question pertains to three tables in particular.
I have books in my library under the table "CATALOGUE" and books reserved by members(many-to-many relationship) as another table "RESERVATIONS", and borrowed books(many-to-many relationship) as another table "BORROWED_BOOKS".
Basically, I have to output all details about books currently in reservations(which means I need to pull data from LIBRARY_BOOKS), as well as the members that have them reserved(data from LIBRARY_MEMBERS) and I also need to list out whether the book is currently on loan or not.
It is the last criteria which I'm having trouble with. I know that to find out whether a reserved book is currently on loan or not, I have to compare it against the BORROWED_BOOKS table but I don't know how to go about it to actually output it.
This is my incomplete code:
SELECT
M.Member_ID,
M.Name_U,
R.Book_ID,
C.ISBN,
C.Title,
C.Description_B,
R.Reservation_date
SOMETHING AS 'Loan Status'
FROM
dbo.Reservations R
INNER JOIN
dbo.Library_Members M ON R.Member_ID = M.Member_ID
INNER JOIN
dbo.[Library_Books] B ON R.Book_ID = B.Book_ID
INNER JOIN
dbo.Catalogue C ON C.ISBN = B.ISBN
ORDER BY M.Member_ID ASC
I need help at the last line of SELECT. How do I successfully compare the two tables RESERVATIONS and BORROWED_BOOKS, and return some kind of flag(?) which I can then output to indicate whether it is currently reserved or not? And also, how would I structure the JOINof that particular comparison?
The columns in common between RESERVATIONS and BORROWED_BOOKS are: Book_ID. It's not a relationship, it's just that they both share this column.
You can EXISTS
EXISTS( SELECT * FROM BORROWED_BOOKS WHERE Book_ID = R.Book_ID)
example
SELECT
M.Member_ID,
M.Name_U,
R.Book_ID,
C.ISBN,
C.Title,
C.Description_B,
R.Reservation_date,
EXISTS( SELECT * FROM BORROWED_BOOKS WHERE Book_ID = R.Book_ID) as reserved,
SOMETHING AS 'Loan Status'
FROM
dbo.Reservations R
INNER JOIN
dbo.Library_Members M ON R.Member_ID = M.Member_ID
INNER JOIN
dbo.[Library_Books] B ON R.Book_ID = B.Book_ID
INNER JOIN
dbo.Catalogue C ON C.ISBN = B.ISBN
ORDER BY M.Member_ID ASC

SQL statement - difficulty producing two fields from one table

I am relatively new to SQL and able to write some simple statements with some JOIN, WHERE thrown in. I also have some experience with SSRS 2008. However I am having difficulty producing two (unique) lists of names based on one table.
I am trying to produce a report containing Staff Members which lists Clients they look after. All names (regardless whether Staff or Client) are held in the Person table. I can run a simple query listing all staff members or clients but I am unable to list both.
To produce the Client list my query would be simply
SELECT p.Forenames, p.Surname
FROM Person AS p
INNER JOIN Client AS c ON p.ID = c.ID
and to produce the Staff list my query would be as above but the INNER JOIN as follows:
INNER JOIN StaffMember AS s ON p.ID = s.ID
The link between Staff Member and Client with all the different links are as follows (for reference).
Client.ID = ClientRecordForm.Client_ID
Person.ID = ClientRecordForm.AssignedSupportWorker_ID
Person.ID = StaffMember.ID
StaffMember.ID = ClientRecordForm.AssignedSupportWorker_ID
To help illustrate this, I can run a query to list Staff Members who have been assigned to Clients.
SELECT DISTINCT p.Forenames, p.Surname
FROM Person AS p
INNER JOIN ClientRecordForm AS crf ON p.ID = crf.AssignedSupportWorker_ID
This last query is essentially what I want but I am struggling to add the Client names as I don't seem to be able to distinguish the Clients as I am already using the Person table
If you want to show persons and be able to distinguish clients as well, try a self join.
SELECT DISTINCT p.Forenames, p.Surname
FROM Person AS p
INNER JOIN ClientRecordForm AS crf ON p.ID = crf.AssignedSupportWorker_ID
inner join person as personClients on crf.clientid = personClients.id
As you can see, you could then join another persons table to get StaffMember, you can join from personClients to Client to get information there ...etc.
Is that what you want?
SELECT DISTINCT p.Forenames, p.Surname
FROM Person AS p
INNER JOIN ClientRecordForm AS crf
ON p.ID = crf.AssignedSupportWorker_ID
INNER JOIN Client AS c
ON c.ID = crf.Client_ID

Joining multiple tables and getting multiple attributes from one of them

I'm trying to join multiple tables together for building a report. The report lists a course, revisions made to it, and who requested, made and approved the revisions.
Under requested, made an approved, the values are employee numbers. I'm trying to join my innerjoined table above, with the Employee table so I can list the names (not just employee numbers) of those that requested, made and approved revisions.
This is what I have which I know is totally wrong.
SELECT *
FROM Courses
INNER JOIN CourseRevisions ON CourseRevisions.PELID = Courses.PELID
INNER JOIN CourseGroups ON CourseGroups.CourseGroupID = Courses.CourseGroupID
INNER JOIN [dbo].[OPG_Employees] ON OPG_Employees.EmployeeID = CourseRevisions.UpdatedBy
AND OPG_Employees.EmployeeID = CourseRevisions.ApprovedBy
AND OPG_Employees.EmployeeID = CourseRevisions.RequestedBy
This only returns a single result which just happens to have the same employee ID listed for all 3 (Requested, Approved and Updated)
How would i get it so I can get the table result for individual employees in each?
You have to join to the OPG_Employees table once for each field, i.e. 3 times in the example above. One INNER JOIN to it for UpdatedBy, one INNER JOIN for ApprovedBy, one INNER JOIN for RequestedBy.
Something like so:
SELECT *
FROM Courses
INNER JOIN CourseRevisions ON CourseRevisions.PELID = Courses.PELID
INNER JOIN CourseGroups ON CourseGroups.CourseGroupID = Courses.CourseGroupID
INNER JOIN [dbo].[OPG_Employees] empUpdatedBy ON empUpdatedBy.EmployeeID = CourseRevisions.UpdatedBy
INNER JOIN [dbo].[OPG_Employees] empApprovedBy ON empApprovedBy.EmployeeID = CourseRevisions.ApprovedBy
INNER JOIN [dbo].[OPG_Employees] empRequestedBy ON empRequestedBy.EmployeeID = CourseRevisions.RequestedBy
You need a separate join for each employee being referenced:
SELECT *
FROM Courses INNER JOIN
CourseRevisions
ON CourseRevisions.PELID = Courses.PELID INNER JOIN
CourseGroups
ON CourseGroups.CourseGroupID = Courses.CourseGroupID INNER JOIN
[dbo].[OPG_Employees] UpdateEmp
ON UpdateEmp.EmployeeID = CourseRevisions.UpdatedBy INNER JOIN
[dbo].[OPG_Employees] ApprovedEmp
on OPG_ApprovedEmp.EmployeeID = CourseRevisions.ApprovedBy INNER JOIN
[dbo].[OPG_Employees] RequestedEmp
on RequestedEmp.EmployeeID = CourseRevisions.RequestedBy
Your original formulation required that all three ids be exactly the same.

Summing the result of a query

Ok, so I know what this query should give, it should give the sum of the total it is outputting. I am having trouble figuring out how to sum the results. The database i am using is the publishers database
Here is my query
SELECT DISTINCT authors.state, qty*price as total
from authors
JOIN titleauthor on titleauthor.au_id = authors.au_id
JOIN titles on titles.title_id = titleauthor.title_id
JOIN sales on sales.title_id = titles.title_id
JOIN stores on stores.stor_id = sales.stor_id
WHERE authors.state LIKE stores.state
and Here is the output it gives
CA 104.6500
CA 299.8000
CA 299.8500
CA 1000.0000
The joins give you duplicates because they include more than one table representing a many-to-many relationship and the filtering is not sufficient to preclude the many-to-many tables from returning more than one row based on the given join conditions.
You could avoid duplicates by introducing a semi-join in the form of an EXISTS predicate and subsequent moving some of the tables there. Here's one possible way of using EXISTS in your situation:
SELECT
stores.state,
SUM(qty * price)
FROM sales
INNER JOIN stores ON sales.stor_id = stores.stor_id
INNER JOIN titles ON sales.title_id = stores.title_id
WHERE EXISTS (
SELECT *
FROM authors a
INNER JOIN titelauthor ta ON a.au_id = ta.au_id
WHERE ta.titel_id = titles.title_id
AND a.state LIKE stores.state
)
GROUP BY
stores.state
The central table is most likely sales, it's where the figures come from. Therefore, the query is being built around sales. Other tables are joined explicitly (using a JOIN clause) as long as they return only one row for every row of sales. Once a table is going to return more than one row, it gets moved to EXISTS.
There's one more thing. While working on this query, I noticed that one join may be redundant (in your query as well as in mine). The table is titles. If your foreign keys are in order, you shouldn't need to join to titles, as titleauthor could be joined directly to stores on title_id. (Even if you don't have the corresponding foreign keys, you still don't have to include titles, as any possible non-existent titles referenced by either titleauthor or sales would be filtered out either way.)
So the final query might look like this:
SELECT
stores.state,
SUM(qty * price)
FROM sales
INNER JOIN stores ON sales.stor_id = stores.stor_id
WHERE EXISTS (
SELECT *
FROM authors a
INNER JOIN titelauthor ta ON a.au_id = ta.au_id
WHERE ta.titel_id = sales.title_id
AND a.state LIKE stores.state
)
GROUP BY
stores.state
SELECT DISTINCT authors.state, sum(qty*price) as total
from authors
JOIN titleauthor on titleauthor.au_id = authors.au_id
JOIN titles on titles.title_id = titleauthor.title_id
JOIN sales on sales.title_id = titles.title_id
JOIN stores on stores.stor_id = sales.stor_id
WHERE authors.state = stores.state
group by authors.stat