Conditional Table Join In SQL Server - sql

I have a table named 'Task' with fields (Id int, TaskName nvarchar, AssigneeType int, AssigneeId int).
AssigneeType can contain 3 int values pointing to specific tables. (0 = User, 1 = Group, 2 = Location)
User, Group, Location are the tables
AssigneeId contains the Id of record in the table pointed by AssigneeType.
Problem Area
I want to extract all tasks by joining task table with the table pointed by AssigneeType.
If AssigneeType contains 0, I need to join Task table with User table.
If AssigneeType contains 1, I need to join Task table with Group table.
If AssigneeType contains 2, I need to join Task table with Location table.
Basically I need to make conditionally join. I have found this, but I dont know that how can I incorporate for my need. I want to show TaskName and Joined Table Record's Name field.
Any Help?

This will do a left join and give you the first name it finds using COALESCE
SELECT Task.*, COALESCE([User].Name, [Group].Name, Location.Name) AS Name
FROM Task
LEFT JOIN [User]
ON [User].Id = Task.AssigneeId
AND Task.AssigneeType = 0
LEFT JOIN [Group]
ON [Group].Id = Task.AssigneeId
AND Task.AssigneeType = 1
LEFT JOIN Location
ON Location.Id = Task.AssigneeId
AND Task.AssigneeType = 2

You cannot join a table or not. You must join all tables. So you will outer join the three tables getting only one match. Then show that match with COALESCE.
select t.*, coalesce(u.name, g.name, l.name) as name
from task t
left join user u on t.assigneetype = 0 and t.assigneeid = u.id
left join [group] g on t.assigneetype = 1 and t.assigneeid = g.id
left join location l on t.assigneetype = 2 and t.assigneeid = l.id;
EDIT: I've corrected my answer and replaced backticks with brackets. Different dbms use different symbols in order to use reserved words such as 'GROUP' for table names. In SQL Server this should be brackets rather than backticks. However, it is always a bad idea to use reserved words for table names and columns, so you might want to change this, if you can.

Related

IS it possible to inner join 3 tables for SQL?

I'm trying to fetch certain data by using a column that exists in all 3 tables, I have three tables
[Txn].[TxnPaymentResponse]
[Txn].[Txn]
[Txn].[TxnLineItem]
Right now if I run the query, I'm able to get the correct data only userID.
But I want to grab another piece of information (suppose that call column X) from the 3rd table (TxnLineItem). That column doesn't exist in the first 2 tables. In this scenario how could I perform inner join and show that piece of info in the query?
DECLARE #CompletedTransactionSince DATETIME2(7) = '2022-09-13 00:00:00.000000'
SELECT DISTINCT
t.UserID
FROM
[Txn].[Txn] T WITH(NOLOCK)
INNER JOIN
[Txn].[TxnPaymentResponse] TPR WITH(NOLOCK) ON T.[TxnID] = TPR.[TxnID]
WHERE
TPR.[PaymentResponseType] = 'FINAL'
AND TPR.[AuthorizedAmount] > CONVERT(DECIMAL(9,3), 0)
AND (#CompletedTransactionSince IS NULL OR
T.[CreatedOn] > #CompletedTransactionSince)
Results from my query:
UserID
C1671FDA-70A8-4C07-BBDF-ACD06ADD145F
Table 3:
TxnID
StandardProductCategory
6FE0D0D0-9959-41AA-9BF0-00000003DED8
Carwash
D1B0EA51-C476-488C-A140-0000C1C7D099
General
Suppose I'm doing inner join like
INNER JOIN
[Txn].[TxnLineItem] TXL WITH(NOLOCK) ON T.[TxnID] = [Txn].[TxnID],
But my I want to grab the X column that has the same transactionID. I want to display UserID that has a Carwash only. Not sure if it's possible to write another clause with an inner join.
As per "I want to display UserID that has a Carwash only", you need to exclude records.
Just add these EXISTS and NOT EXISTS clauses at the end of to query.
EXISTS part is filter users that has CarWash.
NOT EXISTS part is exclude users that has StandardProduct Category records other than Car Wash
......
AND (#CompletedTransactionSince IS NULL OR
T.[CreatedOn] > #CompletedTransactionSince)
AND EXISTS ( SELECT 1 FROM [Txn].[TxnLineItem] TXL
WHERE T.[TxnID] = [Txn].[TxnID]
AND TXL.StandardProductCategory='Carwash')
AND NOT EXISTS( SELECT 1 FROM [Txn].[TxnLineItem] TXL
WHERE T.[TxnID] = [Txn].[TxnID]
AND TXL.StandardProductCategory=<>'Carwash')

CASE WHEN Statement with two tables with table 2 column referencing has non-unique value

I have a query that I have worked on and only one section has caused me fits. I am trying to create a column within the query based on the values of two tables. I have tried CASE WHEN and it functions, but due to the non-unique values involved, the row count in the query between the original query without increases. For example, this is the case when that I have written:
Select r.Id,
r.RequiredOn AT TIME ZONE 'UTC' AT TIME ZONE 'Central Standard Time'
as RequiredDate,
Concat(vs.Salutation, ' ',vs.FirstName, ' ', vs.LastName) as Name,
oo.Name as RequestingOrganization,
o.Name as Location,
Case
When r.IntendedOutcome = '1' Then 'T'
When r.IntendedOutcome = '2' Then 'R'
End as RequestType,
etr.TypeRequested,
Case
When etr.Identifier is not null then etr.Identifier
When etr.Identifier is null then ' '
End as Identifier,
f.OfferedOn,
f.OfferResponse,
r.DestinationCountryCodes,
o.Id,
CASE
WHEN o.Id = oir.OrganizationId AND oir.OrganizationRoleId =
'de51c814-f86d-49c9-941b-999a98be4894'
THEN 1
ELSE NULL
END AS Bk1
From [Request] r
Left Join Recovered etr
on etr.DistributionRequestId = r.Id
Left Join [Offer] f
on f.Id = etr.Id
Left Join [dbo].Contact vs
on vs.Id = r.SId
Left Join [dbo].Organization o
on o.Id = r.SLocationId or o.Id = r.RLocationId
Left Join [dbo].Organization oo
on oo.Id = r.RequestingOrganizationId
Left Join dbo.OrganizationInRole oir
on oir.OrganizationId = o.Id
Where f.Response = 'Accepted' or f.Response is NULL
The picture shows that the OrganizationId is not unique with this table and therefore when an OrganizationId is matched and the OrganizationRoleId is found, it is bringing all of the OrganizationRoleId's over in the query and adding to it rather than just seeing that it has the particular Role ID and adding to the one row I need it to.
The Organization Role column in non-unique and every organization can multiple roles(sometimes 4-5). I need that if the OrganizationId is A and the matching OrganizationId in Table 2 has the identifier in the OrganizationRole column, then add a 1.
The Organization table (Table 2) has a OrganizationId column and a OrganizationRole column. The OrganizationId is non-unique as the OrgnanizationId could be used in 5 consecutive rows since that organization has 5 Roles.
The results that I am getting are that the query is pulling all of the Roles from Organizations that do match that table. It basically added 33% more rows to the query versus the original.
When you say
... if the OrganizationId is A and the matching OrganizationId in Table 2 has the identifier in the OrganizationRole column, then add a 1.
Are you wanting to create a count of the number if times this condition is true? If so, you need to wrap your CASE in an aggregate function and group on the other rows.
Alternatively, as Stu suggests in the comments, you could pre-aggregate the OrganizationInRole table, filtering for the role you are actually interested in; something like
SELECT r.Id,
...
oir.RoleCount AS Bk1
FROM [Request] r
...
LEFT JOIN [dbo].Organization o
ON o.Id = r.SLocationId or o.Id = r.RLocationId
...
LEFT JOIN (
SELECT OrganizationId, COUNT(*) AS RoleCount
FROM dbo.OrganizationInRole
WHERE OrganizationRoleId = 'de51c814-f86d-49c9-941b-999a98be4894'
GROUP BY OrganizationId) AS oir ON oir.OrganizationId = o.Id
...
You can do this for any other table which has multiple related rows, reducing them to a single row to join to and removing the need for aggregation and grouping in the main query.

SQLite select query if inner join query doesn't exists

I have two tables, one for all the foods which contains food id, default name and other foods values.
Another table is for the food name translations which contains food id, language id, translation.
What i want to do is join between these tables by the food id to get the translation for food id = 5 for example and language id = 1 which is Spanish for example. and i do it by:
SELECT *
FROM Foods
INNER JOIN FoodNameTranslations USING(Food_ID)
WHERE Food_ID = 5
AND Language_ID = 1
Now what if the the 'FoodNameTranslations' table doesn't have a translation for Food_ID 5 and Language 1?
then i want to simply get the food row with food id = 5 from the 'Foods' table (that contains the default name).
How can i make one query that does this? thanks!
You would do a LEFT JOIN and put the language ID into the join condition.
SELECT
COALESCE(t.TranslatedName, f.DefaultName) FoodName
FROM
Foods f
LEFT JOIN FoodNameTranslations t ON t.Food_ID = f.Food_ID AND t.Language_ID = 1
WHERE
f.Food_ID = 5
You cando do this by changing the JOIN condition:
INNER JOIN gets all records that are common between both tables based on the supplied ON clause.
LEFT JOIN gets all records from the LEFT linked and the related record from the right table ,but if you have selected some columns from the RIGHT table, if there is no related records, these columns will contain NULL.
RIGHT JOIN is like the above but gets all records in the RIGHT table.
FULL JOIN gets all records from both tables and puts NULL in the columns where related records do not exist in the opposite table.
Try using LEFT JOIN instead INNER JOIN, you query will be like this:
SELECT *
FROM Foods
LEFT JOIN FoodNameTranslations USING(Food_ID)
WHERE Food_ID = 5
AND Language_ID = 1

I want to retrieve data from 4 tables in SQL Server

I want to retrieve data from 4 tables. Patient table has id as PK which is the foreign key in other three tables ett, phar and ssc. Where a patient lie in only one category. i.e patient id pt1 exists in either of the 3 tables. now I want to retrieve patient info along with its associated category.
My query is:
SELECT *
FROM Patient p
INNER JOIN ETT t
ON p.Patient_ID = t.Patient_ID || INNER JOIN Pharmacological ph
ON p.Patient_ID = ph.Patient_ID
I used OR clause because I want only 1 inner join executing at one time. but its not giving me results, any suggestions??
....Patient table has ID as PK which is the foreign key in other three
tables name: ett, phar and ssc where a patient lie in only one
category. Example, patient id pt1 exists in either of the 3 tables.
Based on your statement, you can join all the tables in table Patient using LEFT JOIN since a record can only exist on one table. The query below uses COALESCE which returns the first non-null value with int the list.
The only thing you need is to manually specify the column names that you want to be shown on the list as shown below.
SELECT a.*,
COALESCE(t.colA, p.ColA, s.ColA) ColA,
COALESCE(t.colB, p.ColB, s.ColB) ColB,
COALESCE(t.colN, p.ColN, s.ColN) ColN
FROM Patient a
LEFT JOIN ETT t
ON a.Patient_ID = t.Patient_ID
LEFT JOIN Phar p
ON a.Patient_ID = p.Patient_ID
LEFT JOIN SSC s
ON a.Patient_ID = s.Patient_ID
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
For or - do not ise ||, use "or"
You cannot join with or, you need re-format your query.

Filter a SQL Server table dynamically using multiple joins

I am trying to filter a single table (master) by the values in multiple other tables (filter1, filter2, filter3 ... filterN) using only joins.
I want the following rules to apply:
(A) If one or more rows exist in a filter table, then include only those rows from the master that match the values in the filter table.
(B) If no rows exist in a filter table, then ignore it and return all the rows from the master table.
(C) This solution should work for N filter tables in combination.
(D) Static SQL using JOIN syntax only, no Dynamic SQL.
I'm really trying to get rid of dynamic SQL wherever possible, and this is one of those places I truly think it's possible, but just can't quite figure it out. Note: I have solved this using Dynamic SQL already, and it was fairly easy, but not particularly efficient or elegant.
What I have tried:
Various INNER JOINS between master and filter tables - works for (A) but fails on (B) because the join removes all records from the master (left) side when the filter (right) side has no rows.
LEFT JOINS - Always returns all records from the master (left) side. This fails (A) when some filter tables have records and some do not.
What I really need:
It seems like what I need is to be able to INNER JOIN on each filter table that has 1 or more rows and LEFT JOIN (or not JOIN at all) on each filter table that is empty.
My question: How would I accomplish this without resorting to Dynamic SQL?
In SQL Server 2005+ you could try this:
WITH
filter1 AS (
SELECT DISTINCT
m.ID,
HasMatched = CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END,
AllHasMatched = MAX(CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END) OVER ()
FROM masterdata m
LEFT JOIN filtertable1 f ON join_condition
),
filter2 AS (
SELECT DISTINCT
m.ID,
HasMatched = CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END,
AllHasMatched = MAX(CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END) OVER ()
FROM masterdata m
LEFT JOIN filtertable2 f ON join_condition
),
…
SELECT m.*
FROM masterdata m
INNER JOIN filter1 f1 ON m.ID = f1.ID AND f1.HasMatched = f1.AllHasMatched
INNER JOIN filter2 f2 ON m.ID = f2.ID AND f2.HasMatched = f2.AllHasMatched
…
My understanding is, filter tables without any matches simply must not affect the resulting set. The output should only consist of those masterdata rows that have matched all the filters where matches have taken place.
SELECT *
FROM master_table mt
WHERE (0 = (select count(*) from filter_table_1)
OR mt.id IN (select id from filter_table_1)
AND (0 = (select count(*) from filter_table_2)
OR mt.id IN (select id from filter_table_2)
AND (0 = (select count(*) from filter_table_3)
OR mt.id IN (select id from filter_table_3)
Be warned that this could be inefficient in practice. Unless you have a specific reason to kill your existing, working, solution, I would keep it.
Do inner join to get results for (A) only and do left join to get results for (B) only (you will have to put something like this in the where clause: filterN.column is null) combine results from inner join and left join with UNION.
Left Outer Join - gives you the MISSING entries in master table ....
SELECT * FROM MASTER M
INNER JOIN APPRENTICE A ON A.PK = M.PK
LEFT OUTER JOIN FOREIGN F ON F.FK = M.PK
If FOREIGN has keys that is not a part of MASTER you will have "null columns" where the slots are missing
I think that is what you looking for ...
Mike
First off, it is impossible to have "N number of Joins" or "N number of filters" without resorting to dynamic SQL. The SQL language was not designed for dynamic determination of the entities against which you are querying.
Second, one way to accomplish what you want (but would be built dynamically) would be something along the lines of:
Select ...
From master
Where Exists (
Select 1
From filter_1
Where filter_1 = master.col1
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_1
)
Intersect
Select 1
From filter_2
Where filter_2 = master.col2
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_2
)
...
Intersect
Select 1
From filter_N
Where filter_N = master.colN
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_N
)
)
I have previously posted a - now deleted - answer based on wrong assumptions on you problems.
But I think you could go for a solution where you split your initial search problem into a matter of constructing the set of ids from the master table, and then select the data joining on that set of ids. Here I naturally assume you have a kind of ID on your master table. The filter tables contains the filter values only. This could then be combined into the statement below, where each SELECT in the eligble subset provides a set of master ids, these are unioned to avoid duplicates and that set of ids are joined to the table with data.
SELECT * FROM tblData INNER JOIN
(
SELECT id FROM tblData td
INNER JOIN fa on fa.a = td.a
UNION
SELECT id FROM tblData td
INNER JOIN fb on fb.b = td.b
UNION
SELECT id FROM tblData td
INNER JOIN fc on fc.c = td.c
) eligible ON eligible.id = tblData.id
The test has been made against the tables and values shown below. These are just an appendix.
CREATE TABLE tblData (id int not null primary key identity(1,1), a varchar(40), b datetime, c int)
CREATE TABLE fa (a varchar(40) not null primary key)
CREATE TABLE fb (b datetime not null primary key)
CREATE TABLE fc (c int not null primary key)
Since you have filter tables, I am assuming that these tables are probably dynamically populated from a front-end. This would mean that you have these tables as #temp_table (or even a materialized table, doesn't matter really) in your script before filtering on the master data table.
Personally, I use the below code bit for filtering dynamically without using dynamic SQL.
SELECT *
FROM [masterdata] [m]
INNER JOIN
[filter_table_1] [f1]
ON
[m].[filter_column_1] = ISNULL(NULLIF([f1].[filter_column_1], ''), [m].[filter_column_1])
As you can see, the code NULLs the JOIN condition if the column value is a blank record in the filter table. However, the gist in this is that you will have to actively populate the column value to blank in case you do not have any filter records on which you want to curtail the total set of the master data. Once you have populated the filter table with a blank, the JOIN condition NULLs in those cases and instead joins on itself with the same column from the master data table. This should work for all the cases you mentioned in your question.
I have found this bit of code to be faster in terms of performance.
Hope this helps. Please let me know in the comments.