How to apply conditional join in EF Core? - ef-core-2.1

Let's say I have a table Contacts, Customers, and Suppliers. Contacts has a column ContactTypeId and ExternalContactId. So you could end up with either:
var extContacts = from ct in _context.Contacts
join cu in _context.Customers
on ct.ExternalContactId equals cu.Id
Or I could have this as well
var extContacts = from ct in _context.Contacts
join su in _context.Suppliers
on ct.ExternalContactId equals su.Id
Here, I've showed only 2 joins to illustrate my point. In my case, I have 5 tables.
Is there any way, I can make just one call, join Contacts table depending on the ContactTypeId? Otherwise I may have to make more than 1 call (up to 5).
Something like:
var extContacts = from ct in _context.Contacts
join (
//if type is Customer --> Join with Customers table
//if type is supplier --> join with Suppliers table
)
select name, phone, address //fields common to both tables
Thanks for helping

Related

Two tables. One contains the school info and the other personal. A quire that picks students who are from CA and like either skateboard or soccer.

What currently happens is it just selects students from CA and students who like to skateboard. I need it to return only students who are both from CA and play soccer.
SELECT *
FROM schooldata a
INNER JOIN studentinfo b
ON b.schooldata_id = a.id
WHERE a.state = "ca"
AND ( activity = "soccer"
OR activity = "skateboard" )
You will have to do inner join based on lastname and firstname column ex:-b.lastname=a.lastname and b.firstname=a.firstname . ideally you should be maintaining primary key column of type integer in schooldata table and its foreign key reference in studentinfo and join based on those columns.
You should use join clauses. And I think base on your question. Inner Join is the best clause you should use.
SELECT column_name(s)
FROM table1
INNER JOIN table2 ON table1.column_name = table2.column_name;

Mysql Query Search By Dates

I try to create a query which shows available hotels between checkin and checkout dates. Also when I make a reservation for specific rooms, I set their booking column as 1. That means, when the checkout date comes in real life, a trigger function will set that booked column as 0.
If all rooms of a hotel are booked(booked=1) and checkin-checkout dates of those rooms are specific date(checkin-checkout inputs), then don't put that hotel in the list. My query doesn't show the result that I want.
Query: Inputs: Country(state), checkin and checkout.
SELECT DISTINCT a.* FROM accommodation a INNER JOIN cb_states s ON a.state = s.id
INNER JOIN accommodation_rooms ar ON a.id = ar.accommodation
WHERE state = 1 AND a.id NOT IN
(
SELECT 1 FROM booking b
WHERE
(
(b.arrival_date BETWEEN '2017-11-16' AND '2018-03-16')
OR
(b.departure_date BETWEEN '2017-11-16' AND '2018-03-16')
)
)
When I run the query, it always shows all hotels no matter dates. If I write ... WHERE ar.booked = 0 AND state = 1 AND a.id NOT IN..., then it doesn't show accommodation id 13, but it doesn't show either when I change dates.
accommodation table:
accommodation_rooms table:
booking table:
booked_rooms table:(has foreign key with booking table)
You have multiple issues with your query:
The NOT IN list has SELECT 1. That doesn't give a great variety to the NOT IN list.
Your logic for overlaps is wrong.
I think you need to connect accommodation rooms to booked accommodation rooms.
So:
SELECT DISTINCT a.*
FROM accommodation a INNER JOIN
cb_states s
ON a.state = s.id INNER JOIN
accommodation_rooms ar
ON a.id = ar.accommodation
WHERE s.state = 1 AND
a.id NOT IN (SELECT br.accommodation_room
FROM booking b JOIN
booked_room br
ON b.?? = br.??
WHERE b.arrival_date <= '2018-03-16' AND
b.departure_date >= '2017-11-16'
);

SQL: Returning a list of all the classes that 2 specific students are both attending

I've tried all I can to figure this one out, hopefully someone here can help.
I'm given the database schema as follows:
Customer: ID FirstName LastName DateOfBirth
Instructor: Id FirstName LastName
FitnessClass: Id ClassName Cost InstructorId
Subscription: CustomerId ClassId StartDate
With the question being:
Retrieve a list of all classes (Id and ClassName) that both Joe Bloggs and John Snow have subscribed to (ie Joe Bloggs and John Snow have been in the same Fitness Class).
My attempts have only produced all classes that either are in, not only the classes that both are in.
My attempt:
SELECT fitnessclass.id, fitnessclass.classname
FROM fitnessclass
LEFT JOIN subscription ON fitnessclass.Id = subscription.ClassID
LEFT JOIN customer ON subscription.CustomerID = customer.ID
WHERE customer.ID IS NOT NULL
AND customer.FirstName IN ("Joe", "Bloggs")
We want the c.ClassId and c.ClassName values from DISTINCT rows where:
class c.Id is named c.ClassName and costs c.Cost and is taught by c.InstructorId
AND customer c1.ID is named c1.FirstName c1.LastName and was born on c1.DateOfBirth
AND customer c2.ID is named c2.FirstName c2.LastName and was born on c2.DateOfBirth
AND c1.ID <> c2.ID
AND customer s1.CustomerId subscribed to s1.ClassId starting on s1.StartDate
AND customer s2.CustomerId subscribed to s2.ClassId starting on s2.StartDate
AND c1.ID = s1.CustomerId AND c2.ID = s2.CustomerId
AND c1.FirstName = 'Joe' AND c1.LastName = 'Bloggs'
AND c2.FirstName = 'John' AND c2.LastName = 'Snow'
AND s1.ClassId = c.ClassId AND s2.ClassId = c.ClassId
Observe that an aliased table with no duplicates holds (the set of) rows that make a true statement from a statement template (predicate) associated with its base table by the database designer:
-- class c.Id is named c.ClassName and costs c.Cost and is taught by c.instructorId
FitnessClass c
-- cx.ID is named cx.FirstName cx.LastName and was born on cx.DateOfBirth
Customer cx
-- customer sx.CustomerId subscribed to class sx.ClassId starting on sx.StartDate
Subscription sx
Observe also that if expression L holds the rows satisfying templateL and expression R holds the rows satisfying templateR then
L JOIN R holds the rows satisfying templateL AND templateR
R WHERE condition holds the rows satisfying templateR AND condition
templateR ON condition holds the rows satisfying templateR AND condition
So the rows we want to SELECT from are:
FROM Class c
JOIN Customer c1 JOIN Customer c2
ON c1.ID <> c2.ID
JOIN Subscription s1 JOIN Subscription s2
WHERE
AND c1.ID = s1.CustomerId AND c2.ID = s2.CustomerId
AND c1.FirstName = 'Joe' AND c1.LastName = 'Bloggs'
AND c2.FirstName = 'John' AND c2.LastName = 'Snow'
AND s1.ClassId = c.ClassId AND s2.ClassId = c.ClassId
Conditions can be in ANDed any order as long as each only uses columns from a preceding JOIN or JOIN ON. So you can rearrange these in some other order if you think it better. (Eg to localize the use of some column names.) (But arguments that you must organize via ON (or that "," is inappropriate) are specious.)
DISTINCT removes duplicate rows after dropping the non-SELECTED columns from the table that the FROM etc produces. This is so that the result holds the set of rows that satisfy its template. DISTINCT isn't always necessary. But you still want distinct rows. In general you have to think about whether you can avoid DISTINCT. Sometimes you can't. Sometimes you can reason that what you do next with a table with duplicates gives the same answer whether or not there are duplicates. Rarely, the requested result is permitted to have or is supposed to have duplicates. But then you can't use that result further while reasoning via the simple relational-template expression correspondence. (Exercise: Show whether there is a SELECT returning the right classId & className values from the FROM etc table without DISTINCT.)
(It's not clear why you think that LEFT JOIN is appropriate. It returns what JOIN does but with unmatched left table rows extended by NULLs.)

sql - join 2 tables 10 times

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

Database Merging Tables

I just want to ask how to merge two tables.
Because I have a table guest and company both of them acquire a reservation.
I want to join both of them and label them as customers and I want to add a new column customer type which will say guest, if my company is null... and company, if my guest is null...
Here's an example:
guest(g_id(PK), name, guest details etc....)
company(c_id(PK), name, company details etc...)
reservation(r_id(PK), g_id(fk), c_id(fk), reservation details etc...)
When I join them (assuming guest has acquired a reservation) my table looks like
reservation_id|company name|guest name|reservation details
1 null <name> <details>
2 null <name> <details>
What I want is to make it like this:
reservation_id|customer name|cust_type|reservation details
1 <name> <guest> <details>
2 <name> <company> <details>
One possible solution would be to first create a query for each type of reservation (which you might already have) and add the cust_type there, then union the two together. So the initial queries would be something like:
guest_reservations:
SELECT reservation.r_id, guest.name, "guest" AS cust_type, {other fields}
FROM guest, reservation INNER JOIN guest ON guest.g_id = reservation.g_id;
company_reservations:
SELECT reservation.r_id, company.name, "company" AS cust_type {other fields}
FROM company, reservation INNER JOIN company ON company.c_id = reservation.c_id;
Then you could union the two queries together, something like below.
SELECT reservation.r_id, guest.name, "guest" AS cust_type, {other fields}
FROM guest, reservation INNER JOIN guest ON guest.g_id = reservation.g_id
UNION SELECT reservation.r_id, company.name, "company" AS cust_type {other fields}
FROM company, reservation INNER JOIN company ON company.c_id = reservation.c_id;
Of course remember to either remove {other fields} or add in whatever else you need there.
Not sure the schema is the best but consider the following tables:
Res (resId, description, cid, gid, details)
1 Guest Res 1 NULL Just some guy
2 Comp Res NULL 1 Corporate jerk
Guest (gid, name)
1 Chuck
Company (cid, name)
1 Tenth
Check out the parameters of the select query:
1. r.rid : id of reservation table
2. isnull(c.name, g.name) : gets the non null value from either company or guest
3. isnull(replace(c.name, c.name, 'Company'), 'Guest') as type
Fanciness...if c.name is something, then 'Company', otherwise 'Guest'
4. r.details : reservation details
You need the outer join because of the nulls, this gives you a complete view of your data.
left outer join company c on r.cid = c.cid
left outer join guest g on r.gid = g.gid
So if you stick it all together you get what you are looking for:
select r.rid, r.description, isnull(c.name, g.name),
isnull(replace(c.name, c.name, 'Company'), 'Guest') as type,
r.details from res r
left outer join company c on r.cid = c.cid
left outer join guest g on r.gid = g.gid
1 Guest Res Chuck Guest Just some guy
2 Comp Res Tenth Company Corporate jerk