SQL: Isnull Function - sql-server

I'm trying to update an table (se code below).
Where, when a match I want to extract the UserID, if there's no match I want to it to be given the value 6000.
However, the script doesn't do what I would expect it to do.
If U.UserID doesn't exist it doesn't write any value (6000), not if it's null.
How can I go around the problem?
/* Ensure Person (UserID)*/
UPDATE #List
SET #List.UserID = ISNULL(U.UserID, '6000')
FROM #List
INNER JOIN table1 t1 ON #List.ID = t1.ID
INNER JOIN User U ON U.FirstName + ' ' + U.Surname = t1.PersonFullname

According to me problem with joining the table with INNER JOIN i.e this will bring only matching records, Now if you want to updated all the records from the First table not only matching as well as other records where UserID is null then solution to this problem is Use of LEFT JOIN
UPDATE List
SET List.UserID = ISNULL(U.UserID, '6000')
FROM #iList List
LEFT JOIN table1 t1 ON List.ID = t1.ID
LEFT JOIN User U ON U.FirstName + ' ' + U.Surname = t1.PersonFullname

Related

Join on Table Variable if it has data

I have a stored procedure that accepts a user defined table type, which is just a list of ints.
If the table is not set (Either sent in as NULL, or has no rows), then my existing query to return data is OK. But I need to only return Ids that are in that table variable.
So if the table variable has data, I would like to INNER JOIN on it, to only return matching Ids.
So that the moment, it's a basic query like this: (Example)
SELECT ...
FROM MyTable
Where UserId = 1
But I need to somehow:
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1
Can I do the inner join, only when there's data in #MyTableVriable? Or maybe EXITSTS would help?
Of course, a relatively simple approach (assuming your SELECT statement is not too complicated and you don't mind "duplicating" it) would be:
IF EXISTS (SELECT 1 FROM #MyTableVariable)
BEGIN
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1;
END
ELSE
BEGIN
SELECT ...
FROM MyTable m
Where UserId = 1;
END
Otherwise
SELECT ...
FROM MyTable m
LEFT OUTER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1
AND
(
(EXISTS (SELECT 1 FROM #MyTableVariable)
AND v.Id Is Not NULL)
OR
(NOT EXISTS (SELECT 1 FROM #MyTableVariable))
);
I'll stand corrected if I'm wrong (just don't have the opportunity to test it myself right now), but I believe that even if NULL was passed in as the #MyTableVariable value, SQL would still see it as an empty table (with the corresponding structure of the UDT)
SQL Server provides conditional statemets; then, you can try this approach bellow, with a conditional select, based on your table variable status (in this example, checking if is not empty):
IF(EXISTS(SELECT 1 FROM #MyTableVariable))
BEGIN
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id
WHERE UserId = 1
END
ELSE
BEGIN
SELECT ...
FROM MyTable
WHERE UserId = 1
END

SQL Server left join if condition met

I have a problem with my query. I have to do left outer join only if condition is true. If condition is false do another left outer join.
I tried with this but not successfully:
select
*
from
works with(nolock)
if work.type = 1
begin
left outer join
users with(nolock) on users.id = work.owner
else
left outer join
groups with(nolock) on groups.id = work.owner
end
How can I solve this problem?
you should try left join both of them, but inside of the select, take what you want depending on the use case.
SELECT
*,
CASE work.type WHEN '1' THEN 'a.owner' ELSE 'b.owner' END AS owner
FROM
blahblah
left join users on blahblah.user_id = users.id as a,
left join groups as blahblah.groups_id = groups.id as b
You can try below query.
First method:
select
works.*, isnull(users.id, groups.id)
from
works with(nolock)
left outer join
users with(nolock) on users.id = works.owner and work.type = 1
left outer join
groups with(nolock) on groups.id = works.owner
Second method:
if exists (select 1 from works with (nolock) where works.type = 1)
select *
from works with(nolock)
left outer join users with(nolock) on users.id = works.owner
else
select *
from works with(nolock)
left outer join groups with(nolock) on groups.id = works.owner
third method: use dynamic SQL to build query at runtime.
Considering that you want to display all columns (*), then you can conditionally join against both tables by checking the work.type as a join condition:
select
*
from
works
left join users on
users.id = work.owner and
work.type = 1
left join groups on
groups.id = work.owner and
(work.type <> 1 OR work.type IS NULL)
A particular row from the works table can only have a particular value for type, so it will join against users or against groups, but never both. The problem with this solution becomes the displayed columns, since we are joining against both tables, now you have to unify groups and users columns.
You can do this with a bunch of ISNULLs:
select
works.*,
Column1 = ISNULL(users.Column1, groups.Column1),
Column2 = ISNULL(users.Column2, groups.Column2)
from
works
left join users on
users.id = work.owner and
work.type = 1
left join groups on
groups.id = work.owner and
(work.type <> 1 OR work.type IS NULL)
If you need to use the same select repeatedly, you can create a table-valued function to wrap this up so you don't have to code it every time. I'll use the table example to show another alternative, using UNION ALL.
CREATE FUNCTION dbo.GetWorkData (#owner INT) -- assuming its a INT
RETURNS TABLE
AS
RETURN
SELECT
-- Your wanted columns here
FROM
works AS W
INNER JOIN users AS U ON W.owner = U.owner
WHERE
W.owner = #owner AND
W.type = 1
UNION ALL
SELECT
-- Your wanted columns here (must be same data type and order of previous SELECT)
FROM
works AS W
INNER JOIN groups AS U ON W.owner = U.owner
WHERE
W.owner = #owner AND
(W.type <> 1 OR W.type IS NULL)
You can use the function with APPLY:
SELECT
D.*
FROM
works AS W
CROSS APPLY dbo.GetWorkData(W.owner) AS D -- User "OUTER APPLY" if you want works that have no users or groups
You can look into Dynamic SQL. The main idea is that the SQL statement is constructed and compiled at runtime.
You can start here:
Link 1 - MSSQL tips.
or here: Link 2 - official microsoft documentation.

SQL - Apply only one set of joins at a time

I have three joins in my query. My requirement is to select records if either first two joins get satisfied or the third join gets satisfied
select * from security.requestview r
-- either below two joins satisfy
left join security.RequestDelegateView rd on r.id = rd.RequestId
join (select top 1 PersonnelNumber from SECURITY.MyRolesView) m on (m.PersonnelNumber=r.RequestorId or m.PersonnelNumber=r.InitiatorId or m.PersonnelNumber=rd.DelegatePersonnelNumber)
-- or this join
join (select substring(ltrim(DDSUCode),0,3) as Division from security.staffview where PersonnelNumber = (select top 1 PersonnelNumber from SECURITY.MyRolesView)) s
on r.OrganizationUnitRefId like s.Division+'%'
But ofcourse it will try to satisfy all the joins together.
Is there any way I can put some condition where it will select record if either first two joins satisfy or the last join alone satisfies?
Update
I tried putting them as where conditions but then the query is running forever
To answer this question:
Is there any way I can put some condition where it will select record
if either first two joins satisfy or the last join alone satisfies?
No, I know of no way to do this in a single query.
One way you can write this so that the third join only gets executed if the first one doesn't return records is in a multi-query series that populates a temp table or table variable:
INSERT INTO #tmp
SELECT (the first join);
IF (SELECT COUNT(*) FROM #tmp) = 0
INSERT INTO #tmp
SELECT (the second join);
SELECT * FROM #tmp;
WITH FirstJoin AS (
select * from security.requestview r
left join security.RequestDelegateView rd on r.id = rd.RequestId
join (select top 1 PersonnelNumber from SECURITY.MyRolesView) m on (m.PersonnelNumber=r.RequestorId or m.PersonnelNumber=r.InitiatorId or m.PersonnelNumber=rd.DelegatePersonnelNumber)
), SecondJoin AS (
select * from security.requestview r
join (select substring(ltrim(DDSUCode),0,3) as Division from security.staffview where PersonnelNumber = (select top 1 PersonnelNumber from SECURITY.MyRolesView)) s
on r.OrganizationUnitRefId like s.Division+'%'
), BothJoins AS (
SELECT * FROM FirstJoin
UNION ALL
SELECT * FROM SecondJoin
)
SELECT DISTINCT * FROM BothJoins
To get this to work you need to Change the "Select *" inside the CTE's so that every returned column is named. (Since I don't know whats in your tables I couldn't do it).
Please note that FirstJoin and SecondJoin needs to return the same columns.
select *
from table1
left join table2
on table1.id = table2.t1
left join table3
on table1.id = table3.t1
left join table4
on table1.id = table4.t1
where table2.t1 is not null
or table3.t1 is not null
or table4.t1 is not null

Count of rows from a joined table SQL Server

Is it possible to make a count of rows (I am using a view) with a resultset of a subquery?
Assuming I have
Table1
t1id
Table2
id, t1id_Parent, t1id_Child
Table3
t1id
I query for
select
t1.t1id as id,
stuff((select ', ' + CAST(CASE WHEN t2.t1id_Child is not null THEN t2.t1id_Child ELSE '0' END AS varchar) from Table1 t where t.t1id_Parent = t1.t1id for xml path('')),1,2,'') as ids
FROM
Table1 as t1
LEFT OUTER JOIN
Table1 AS t2 ON t1.t1id = t2.t1id_Parent
So I have the needed ids combined into a string that looks like this '1,2,5,9' in the column named "ids" that is working correctly however what I need to do now is have this query
select
t1.t1id as id,
stuff((select ', ' + CAST(CASE WHEN t2.t1id_Child is not null THEN t2.t1id_Child ELSE '0' END AS varchar) from Table1 t where t.t1id_Parent = t1.t1id for xml path('')),1,2,'') as ids
FROM
Table1 as t1
LEFT OUTER JOIN
Table2 AS t2 ON t2.t1id_Parent = t1.t1id
LEFT OUTER JOIN
(select count(t1id) as rows_count, t1id
from Table3
group by t1id) as docs ON docs.t1id in (ids)
to retrieve the row count from the 3rd table using the temporary column however I am getting an error of Invalid column name 'ids'
Is this possible or do I need to first run that query and then go back and run a new one for the count of the third table?
The result I need is
Table1.t1id, count(Table3.t1id)
Table3.t1id comes from Table1.t1id and all Table2.t1id_Child relation through Table1.t1id = Table2.t1id_Parent
I guess not possible, but got it partially working with CTE, I am now getting the list of say I have this '15673,15690,90987,45058' I can then do the query for the third column but getting the error
WITH items AS
(
select
a.ItemID,
stuff(
(select ',' +
CAST(
CASE WHEN b.[ChildItemID] is not null
THEN b.[ChildItemID] ELSE 0 END AS varchar)
from Table2 t2
where t2.[ParentItemID] = a.ItemID
for xml path('')
),1,2,'')
select
items.*,
docs.docs_count
from items
LEFT OUTER JOIN (
select count([DocID]) as docs_count from Table3
group by DocID
) as docs ON docs.ItemID in (items.ItemID)
Conversion failed when converting the nvarchar value '15673,' to data type int.
Note that the issue is not concatenating, I am getting the result expected there, a comma separated list of values, the issue comes when I try to use the comma separated values in "as docs ON docs.ItemID in (items.ItemIDs)"

Determine table to join based on the condition

I am not sure if I've missed to search properly, but I couldn't get the exact question as mine
This is something similar but not exact
https://stackoverflow.com/questions/11533636/determining-which-table-to-join-to
Actually I want to decide with which table to join based on the parameter passed to stored procedure, case when didn't work
Kind of
select * from Table1
left join (case when #Parameter<>NULL Then Table2 else Table3 end) final
on Table1.Col1 = final.Col1
Table2 and Table3 has same structure
I can think of a few of options:
1: IF ... ELSE
IF #Parameter IS NULL
SELECT *
FROM T1
INNER JOIN T2 ON T1.ID = T2.ID
ELSE
SELECT *
FROM T1
INNER JOIN T3 ON T1.ID = T3.ID
2: Dynamic T-SQL
DECLARE #SQL NVARCHAR(MAX);
SELECT #SQL = N'SELECT *
FROM T1
INNER JOIN ' + CASE WHEN #Parameter IS NULL THEN N'T2 t' ELSE N'T3 t' END
+ N' ON T1.ID = t.ID';
EXEC sp_executesql #SQL;
3: UNION ALL and subquery.
SELECT *
FROM T1
INNER JOIN
(
SELECT *
FROM T2
WHERE #Parameter IS NULL
UNION ALL
SELECT *
FROM T3
WHERE #Parameter IS NOT NULL
) t ON T1.ID = t.ID
For this last one you'd have to check the plan the optimiser creates to see if it's OK performance wise.
It seems like you're looking for code reuse, so maybe option 2 is your best one. T-SQL doesn't really lend itself to this sort of polymorphism but you can use workarounds in some cases.
Taking a step back, one question to ask is, if the tables have the same structure, maybe you should just use one table? Then you could use #Parameter to just filter the rows you require instead of trying to create dynamic queries.
It's also worth noting that in situations like this you could handle the code-generation at the application layer with ORMs and the like.
Try this:
select *
from (
select data.*,
case when selection=1 then t1.ExtraField
when selection=2 then t2.ExtraField
when selection=3 then t3.ExtraField
else NULL
end as ExtraField
from data
left join t1 on t1.key = data.key and selection=1
left join t2 on t2.key = data.key and selection=2
left join t3 on t3.key = data.key and selection=3
) T
where ExtraField is not NULL
Done with this
SELECT PA.`module_id` as usertype, PA.module_pk_id, (
CASE PA.`module_id`
WHEN '0'
THEN pea.`name`
ELSE CONCAT(u.`first_name`, " ",u.`last_name`)
END
) as name
FROM `placements_approvers` PA
LEFT JOIN
placements_external_approvers pea
ON
PA.module_pk_id = pea.placement_external_approver_id AND PA.`module_id` = 0
LEFT JOIN
users u
ON
PA.module_pk_id = u.user_id AND PA.`module_id` != 0
WHERE PA.placement_id = '494' AND PA.approver_type = 1

Resources