SQL statement - difficulty producing two fields from one table - sql

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

Related

SQL - Select rows based on filters for two different relations

I'm trying to create a daily menu for my users based on all the meals I have available for their hubs today. Here's what my database looks like:
Here's exactly what I want the query to retrieve:
Give me the emails of the users who have daily_email set to True with the information of their company and the hub their company belongs to.
For each user, I need to see the meals available for them along with the information of the restaurant this meal belongs to.
So far I've tried using INNER JOINS and mixing all tables together then doing the filters I want. It gave me what I wanted but with terrible performance. I'm not mainly looking for the optimal solution, but at least one that has acceptable performance.
Any help would be appreciated.
There is no other way than joining all tables based on their relations:
select u.email, c.name, h.name, m.name, r.name
from users u
inner join companies c on c.id = u.company_id
inner join hubs h on h.id = c.hub_id
inner join hubs_meals hm on hm.hub_id = h.id
inner join meals m on m.id = hm.meal_id
inner join restaurants r on r.id = m.restaurant_id
where u.daily_email
Make sure that all the foreign keys are indexed.

How to Query across multiple tables

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.

SQL select for many to many relationship using binding table

I am struggling with writing a select for diagram on the picture.
What I want to do is write a select, which will show me details of a car repair. As you can see in table Repairs there are only 2 attributes but I am not sure if it's necessary to add more, especialy those from employees_list and parts_list, since I want to show repair data for every vehicle by it's plate_number. By repair data I mean repair id, vehicle plate_number, all employees working on the repair and all parts used on the repair. If my diagram is wrong, please help me fix it and I have no idea how to write select for this because of the many to many relation and the use of binding tables.
This is not so hard as it may seem.
First, obviously, we have to select cars:
select vehicles.* from vehicles
then, let's join repairs:
select
vehicles.*
from vehicles
inner join repairs on vehicles.id = repairs.vehicle.id
We don't need data from repairs in resule set, so we just join it but not mention in 'select' part.
Then we have to join parts needed for repair, and info about parts itself:
select
vehicles.*
from vehicles
inner join repairs on vehicles.id = repairs.vehicle.id
inner join parts_list on parts_list.repair_id = repairs.id
inner join parts on parts_list.part_id = parts.id
For that query we get amout of rows equivalent of amount of parts needed for repair. But it would be more easy to handle such data in code if we aggregate all of them into json column. So in result set we willsee something like:
vehicle_id, vehicle_part, parts_needed_as_json
Lets aggregate this:
select
vehicles.*, json_agg(parts.*) as parts_needed
from vehicles
inner join repairs on vehicles.id = repairs.vehicle_id
inner join parts_list on parts_list.repair_id = repairs.id
inner join parts on parts_list.part_id = parts.id
group by vehicles.id, repairs.id
Now you can add same logic for employees:
select
vehicles.*,
json_agg(parts.*) as parts_needed,
json_agg(employes.*) as employees_needed
from vehicles
inner join repairs on vehicles.id = repairs.vehicle.id
inner join parts_list on parts_list.repair_id = repairs.id
inner join parts on parts_list.part_id = parts.id
inner join employees_list on employes_list.repair_id = repairs.id
inner join employees on employees_list.employee_id = employees.id
group by vehicles.id, repairs.id
BTW, I suggest you to rename your tables to lowercase and singulars.
Like: 'repair', 'employee' and 'vehicle';
Also, name your binding tables like: 'repair_part' and 'repair_employee'.
Some people even suggest to arrange related tables in that names by alphabet, like: 'employee_repair' and 'part_repair', but I think it's not required;
Maybe this is a question of taste but in most cases this leads to more readable queries.
I.e, the query above will looks like:
select
vehicle.*,
json_agg(part.*) as parts_needed,
json_agg(employee.*) as employees_needed
from vehicle
inner join repair on vehicle.id = repair.vehicle_id
inner join parts_repair on parts_repair.repair_id = repair.id
inner join part on parts_repair.part_id = part.id
inner join employees_repair on employees_repair.repair_id = repair.id
inner join employee on employees_repair.employee_id = employee.id
group by vehicle.id, repair.id
Note how elegant 'on' conditions looks now: parts_repair.part_id = part.id, parts_repair.part_id = part.id
Sorry for bad english

I need help formatting inner join command in SQL query

Here is my ERD for SQL Server:
https://c2.staticflickr.com/6/5832/23786188186_d6f1d93132_o.jpg
I need to find out which books are associated with each publisher.
USE BookStoreDB
SELECT ProductID
FROM Books
INNER JOIN [Publishers] PublisherID ON PublishersID = ProductID
I'm assuming I just didn't create the INNER JOIN command correctly?
SELECT p.PublisherID, b.ProductID
FROM Books b
INNER JOIN Publishers p ON p.PublisherID = b.PublisherID
The ON part of a join defines how the two tables are related to each other. In your case, it would be:
INNER JOIN Publishers ON (Books.PublisherID = Publishers.PublisherID)
If you put something between the name of the table, and the ON, it's treated as an alias. You can (and should) also use these aliases in your SELECT
FROM Books B
INNER JOIN Publishers P ON (B.PublisherID = P.PublisherID)
Finally, you probably want to select information that will actually tell you who the publisher is:
SELECT B.ISBN, P.CompanyName
FROM Books B
INNER JOIN Publishers P ON (B.PublisherID = P.PublisherID)

SQL JOIN using a mapping table

I have three tables:
COLLECTION
PERSON
PERSON_COLLECTION
where PERSON_COLLECTION is a mapping table id|person_id|collection_id
I now want to select all entries in collection and order them by person.name.
Do I have to join the separate tables with the mapping table first and then do a join again on the results?
SELECT
c.*,
p.Name
FROM
Collection c
JOIN Person_Collection pc ON pc.collection_id = c.id
JOIN Person p ON p.id = pc.person_id
ORDER BY p.Name
Not sure without the table schema but, my take is:
SELECT
c.*,
p.*
FROM
Person_Collection pc
LEFT JOIN Collection c
ON pc.collection_id = c.id
LEFT JOIN Person p
ON pc.person_id = p.id
ORDER BY p.name
The order you join won't break it but depending on which sql product you're using may effect performance.
You need to decide if you want ALL records from both/either table or only records which have a matching mapping entry, this will change the type of join you need to use.