SQL Conditional / Case Joining / Polymorphic Associations? - sql

I'm trying to implement something similar to Ruby on Rails' polymorphic relationships.
I have the following three tables :
Events
Users
Organisations
An event can be owner by either a user or an organisation, so my Events table includes the columns: owner_type and owner_id.
I can easily list all events that belong to either users or organisations through an inner join and where clause, however, is there a way to make the join table conditional based on the value of the owner_type column, allowing all events to be listed together, regardless of owner_type?
I hope that makes sense.
Any advice appreciated.
Thanks.

You can't make the join table conditional, so in this case you would have to join events to both users and organisations and use coalesce to merge the common fields (eg. name) together.
select e.id, coalesce(u.name, o.name) owner_name
from events e
left join users u on e.owner_id = u.id and e.owner_type = 'user'
left join organisations o on e.owner_id = o.id and e.owner_type = 'org'
However, you may consider creating an owners table, which contains both users and organisations, with a structure like (id, type, org_id, name, ...). This would only require a single join, but may complicate other areas of your schema, eg. user membership of an organisation.
An alternative method would be to union the users and organisations tables together and then join once from events.

Owner has columns common to all owner-subtypes.
Person and Organization have columns specific to each one.

How about moving ownership information out of Events into two join tables, EventsUsers and EventsOrganisations (each of which has just two columns, FKs to Events and the apppropriate owning-object table) ? Then you can UNION two queries each of which joins through the join table to the owning-object table.

A bit old, but I think it would be useful, this version performs better in my scenario than multiple joins
select e.id,
case when e.owner_type = 'person' then
(
select p.name from person p
where p.id=e.owner_id
)
else
(
select o.name from organization o
where o.id=e.owner_id
)
end entityName,
e.owner_type
from events e
in postgres you could even build a json of entire related entity

Related

Find user name for multiple user id columns

I am working with an existing database structure (I'm aware the structure might not be perfect, but it is out of my control). I have one table called Users with user id and name among other irrelevant columns.
My problem is with combining users and the table called settlements. The columns in settlements are as following: Case_ID, created_date, approve_date, approving_user_id, control_date, controlling_user_id
How do I interact between the two tables so that my end result shows the names of the ones approving/controlling and not the ID.
I have tried to work with joins but I don't know how to proceed when there are two different users that have approved and controlled.
select
u.user_id, u.agent_name, s.creating_date, s.approving_date,
s.approving_user, s.controlling_date, s.controlling_user
from USERS u
left join SETTLEMENTS s on u.user.id = s.approving_user
I know that I could join them in too two different tables, one called approve and one control but I would prefer to have the end result in the same table.
You want two joins, but you should start with settlements and then bring in the two users tables:
select s.approving_user, s.controlling_date, s.controlling_user,
s.creating_date, s.approving_date,
ua.user_id as approving_user_id, ua.agent_name as approving_agent_name,
uc.user_id as controlling_user_id, uc.agent_name as controlling_agent_name
from settlements s left join
users ua
on ua.user.id = s.approving_user left join
users uc
on uc.user_id = s.controlling_user_id

How to select records from database table which has to user id (created_by_user, given_to_user) and replace users id by usernames?

This is task table:
This is user table:
I want to select user tasks.
I would give from backend ("given_to_user) id.
But The thing is I want that SELECTED data would have usernames instead of Id which is (created_by_user and given_to_user).
SELECTED table would look like this.
Example:
How to achieve what I want?
Or maybe I designed poorly my tables that It is difficult to select data I need? :)
task table has to id values that are foreign keys to user table.
I tried many thinks but couldn't get desired result.
You did not design poorly the tables.
In fact this is common practice to store the ids that reference columns in other tables. You just need to learn to implement joins:
SELECT
task.id, task.title, task.information, user.usename AS created_by, user2.usename AS given_to
FROM
(task INNER JOIN user ON task.created_by_user = user.id)
INNER JOIN user AS user2 ON task.created_by_user = user2.id;
Do you just want two joins?
select t.*, uc.username as created_by_username,
ug.username as given_to_username
from task t left join
users uc
on t.created_by_user = uc.id left join
users ug
on t.given_to_user = ug.id;
This uses left join in case one of the user ids is missing.

Access SQL Query on same table

I have two tables: one called EMP_Names which simply stores ID and Employee_Name and another table called EMP_Main which stores the main data and which refers to EMP_Names via IDs. Amongst other fields EMP_Main has fields called Technician_Name_ID and Leader_Name_ID which is related to EMP_Names. My problem is this: how can i run a query where both Technician_Name_ID and Leader_Name_ID resolve to Names? In other words both ID fields refer to the same EMP_Names.ID but I can only establish one relationship between the two tables.
Don't know if I'm clear because it's difficult to explain ...
You can use join but you need multiple joins.
select em.*, ent.name as technician, enl.name as leader
from (emp_main as em left join
emp_names as ent
on em.technician_name_id = ent.id
) left join
emp_names as enl
on em.leader_name_id = enl.id;
These are left joins in case the fields are not populated for all rows.

Best way to structure SQL Query

I have several database tables:
Client (basic info, first name, last name, etc)
Employer (basic info, employer name, fax, address, etc.)
I then have a junction table linking the two tables if required:
Client_Employer (ClientID, EmployerID)
All of these tables are maintained with confirmed, accurate, clean data.
I have a fourth table that is used for informational purposes only and the data is neither clean, nor reliable as it is supplied by the end user and cannot be confirmed.
ClientEmployer (data supplied by the client regarding their current employer)
I want to write a query that returns Client/Employer data if a record exists in the Client_Employer table, but will also fallback to the ClientEmployer table for employer information if none exists otherwise.
The columns in Employer match exactly the same columns in ClientEmployer.
I have looked at several options using ISNULL(), CASE, IF/ELSE, etc. but just want to see what others think the best, cleanest way to do this will be?
Well, making a few assumptions about the schema for ClientEmployer table, I'd combine a UNION and an EXISTS like this:
SELECT
cl.ClientID as ClientID,
em.EmployerID as employerID,
cl.firstname,
cl.lastname,
em.employername,
em.fax,
em. address
FROM
Client cl,
Employer em,
Client_Employer ce
WHERE
cl.ClientID = ce.ClientID
and em.EmployerID = ce.EmployerID
UNION
SELECT
Clem.ClientID as clientID,
-1 as EmployerID,
clem.firstname, clem.lastname,
clem.employername,
clem.fax,
clem.address
FROM
ClientEmployer clem
WHERE
NOT EXISTS (
SELECT * FROM Client cl, Employer em, Client_Employer ce
WHERE cl.ClientID = ce.ClientID
and em.EmployerID = ce.EmployerID
and clem.ClientID = cl.ClientID
and clem.EmployerName = ce.EmployerName)
I think you should use ISNULL() to LEFT JOIN Employer, like this:
SELECT
Client.*,
Employer.*
FROM Client
LEFT JOIN Client_Employer
ON Client_Employer.client_id = Client.id
LEFT JOIN ClientEmployer
ON ClientEmployer.client_id = Client.id
LEFT JOIN Employer
ON Employer.id = ISNULL(Client_Employer.employer_id, ClientEmployer.employer_id)
WHERE Employer.id IS NOT NULL;
If some Employer.id is NULL (WHERE clause) so there aren't a relationship from a given Client.id in both Client/Employer tables, which means you'll have just the data which is in some of the Client/Employer tables.
Hope it works as you expect.
What about the usage of IF EXISTS? Seems like you could pretty easily retrieve data where it exists, else select from ClientEmployer in your example where it does not exist.

When to use JOINs

It seems to me that there are two scenarios in which to use JOINs:
When data would otherwise be duplicated
When data from one query would otherwise be used in another query
Are these scenarios right? Are there any other scenarios in which to use JOIN?
EDIT: I think I've miscommunicated. I understand how a JOIN works, what I'm not so sure about is when to use one.
JOINS are used to JOIN tables together with related information.
Tipical situations are where you have lets say
A user table where the user has specific security settings. The join would be used such that you can determine which settings the user has.
Users
-UserID
-UserName
UserSecurityRoles
-UserID
-SecurityRoleID
SecurityRoles
-SecurityRoleID
-SecurityRole
SELECT *
FROM Users u INNER JOIN
UserSecurityRoles usr ON u.UserID = usr.UserID INNER JOIN
SecurityRoles sr ON usr.SecurityRoleID = sr.SecurityRoleID
WHERE sr.SecurityRole = 'Admin'
LEFT joins will be used in the cases where you wish to retrieve all the data from the table in the left hand side, and only data from the right that match.
JOINS are used when you start normalizing your table structure. You can crete a table with 100s on columns, where a lot of the data could possibly be NULL, or you can normalize the tables, such that you avoid having too many columns with null values, where you group the appropriate data into table structures.
The answer to this Question has a VERY good link that has graphical display of using JOINs
JOINS are used to return data that is related in a relational database. Data can be related in 3 ways
One to Many relationship (A Person can have many Transactions)
Many to Many relationship (A Doctor can have many Patients, but a Patient can have more than one Doctor)
One to One relationship (One Person can have exactly one Passport number)
JOINS come in various flavours:
AN INNER JOIN will return data from both tables where the keys in each table match
A LEFT JOIN or RIGHT JOIN will return all the rows from one table and matching data from the other table
A CROSS JOIN will return the product of each table
You use joins when you need information from more than one table :)