Using ISNULL in SQL LEFT JOIN to check if the result is null, and if it is, use another value to join - sql

I have a SQL select with a where clause where i want to check if the result is null, and if it is null I want to use another value in the where clause, but i get 0 rows results, even though i know i should get a row as result.
Heres my (updated) SQL code:
DECLARE #LanguageCode NVARCHAR(3);
SET #LanguageCode = 'FR'
SELECT
wi.WorkItemId,
ds.DisplayString AS Team
FROM dbo.WorkItem AS wi
LEFT JOIN dbo.DisplayString AS ds ON ds.ElementID = wi.TierId AND ds.LocaleID = ISNULL(#LanguageCode, 'ENU')
The code above returns data for "#LanguageCode" when there is data to return, but it does not switch to use 'ENU' when there is no data. Thats the problem!
This is also just a sample since this is part of a larger query with lots of left joins where i need the same functionality against "LocaleID". I'm hoping there would be something easy solution to this like the code above.
To clarify what i want to achieve, if the c.LocaleID = #LanguageCode returns null rows i want to use the hardcoded value as in c.LocaleID = ENU.
If i don't use the ISNULL function and only use 'ENU' it returns the expected result.
I would appreciate any help. Thanks.

If I understand correctly, you want one row, either with the specified language code or 'ENU'. If so, use filtering and ORDER BY:
SELECT TOP (1) c.*
FROM dbo.column1 c
WHERE c.rowID = '1234-1234-1234' AND
c.LocaleID IN (#LanguageCode, 'ENU')
ORDER BY (CASE WHEN c.LocaleID = #LanguageCode THEN 1 ELSE 2 END)

I think you're looking for this
select coalesce((SELECT rowID FROM dbo.column1 where c.rowID = '1234-1234-1234' and LocaleID = #LanguageCode),
(SELECT rowID FROM dbo.column1 where c.rowID = '1234-1234-1234' and LocaleID = 'ENU'));

I believe you are looking for something like this:
SELECT *
FROM table_name c
WHERE c.LocaleID =
case when (select count(*)
from table_name tn
where tn.LocaleID = #LanguageCode) = 0 then
'ENU'
else
#LanguageCode
end;
Here is the demo.
Here is a new demo: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=624100ee44f89decc4c6383e92d0a016
In the new demo I have added some more code to show how to use it in left join as a part of the join condition and also I have added the code to show how to use it in where clause when joining two tables and I believe it belongs in the where clause...

Related

Converting a delete statement into a where clause, T-sql

I have removed rows from a results table after it has been built already. I have decided to try to remove the rows from being inserted into the results table in the first place instead.
To remove the appropriate rows from the results table after the fact I used:
if #InterchangeAction = 'HCR'
begin
--Do not allow claims to be output if they have a prior submission marked 'output'
--and the interchange 'output submission action' is marked as 'hold'
delete from #ResultSet
where exists ( select 1 from ClaimSubmissions CS inner join InterchangeInfo I on CS.InterchangeId = I.InterchangeId
where #ResultSet.ClaimId = CS.ClaimId
and CS.InterchangeId = #InterchangeID
and CS.SubmissionStatus = 'OPT'
and CS.OutputDate is not NULL
)
end
This works as I want but I am thinking it would be more efficient to stop the rows from being added in the first place.
I'm going to start my check with:
if #InterchangeAction = 'HCR'
and then concatenate on to the existing where clause but I am unsure on how to convert the delete statement into a where clause?
Any pointers on where to start would be greatly appreciated.
Unless I'm missing something obvious, aren't you just looking for this?:
INSERT #ResultSet
(<Column List>)
SELECT
<Column List>
FROM
WhatHaveYou AS WHY
WHERE NOT EXISTS ( select 1 from ClaimSubmissions CS
inner join InterchangeInfo I
on CS.InterchangeId = I.InterchangeId
where WHY.ClaimId = CS.ClaimId
and CS.InterchangeId = #InterchangeID
and CS.SubmissionStatus = 'OPT'
and CS.OutputDate is not NULL
)

How to optionally apply a WHERE IN clause based on a variable?

I have a stored procedure that needs to filter by a list of ids that are passed as a comma-delimited list (ie '1,2,3').
I want to apply a WHERE IN clause that will match those ids but ONLY if the variable contains anything (IS NOT NULL AND <> '').
Here's a simplified fiddle of the problem: http://sqlfiddle.com/#!18/5f6be/1
It's currently working for single and multiple ids. But when passing '' or NULL it should return everything but it's not returning anything.
The CTEs and pagination stuff is there for a reason, please provide a solution that doesn't change that.
Using DelimitedSplit8k_lead you could achive this by doing:
CREATE PROC YourProc #List varchar(8000) = NULL AS
BEGIN
SELECT {YourColumns}
FROM YourTable YT
OUTER APPLY dbo.DelimitedSplit8k_Lead(#List,',') DS
WHERE DS.item = YT.YourColumn
OR NULLIF(#List,'') IS NULL
OPTION (RECOMPILE);
END
The OUTER APPLY is used, as if NULL is passed the dataset won't be eliminated. the RECOMPILE is there, as it turns into a "Catch-all query" with the addition of handling the NULL.
Why not use JOIN instead of a subquery in the WHERE clause.
set #userIds = nullif(ltrim(#userIds),'')
select u.*
from Users u
left join string_split(#userIds,',') s on u.Id=s.value
where s.value is not null or #userIds is null
The old school method:
WHERE
(#userIds IS NULL OR #userIds = '' OR U.Id IN (SELECT * FROM STRING_SPLIT(#userIds, ',')))
Add OPTION (RECOMPILE) at the end for this to work and trim the plan.
Edit:
Based on comments, this one generates two table scans. It didn't make a difference in my LocalDb setup but don't rely on it regardless.
WHERE U.Id IN
(
SELECT * FROM STRING_SPLIT(#userIds, ',')
UNION ALL
SELECT U.Id WHERE NULLIF(#userIds, '') IS NULL
)

SQL IN() operator with condition inside

I've got table with few numbers inside (or even empty): #states table (value int)
And I need to make SELECT from another table with WHERE clause by definite column.
This column's values must match one of #states numbers or if #states is empty then accept all values (like there is no WHERE condition for this column).
So I tried something like this:
select *
from dbo.tbl_docs docs
where
docs.doc_state in(iif(exists(select 1 from #states), (select value from #states), docs.doc_state))
Unfortunately iif() can't return subquery resulting dataset. I tried different variations with iif() and CASE but it wasn't successful. How to make this condition?
select *
from dbo.tbl_docs docs
where
(
(select count(*) from #states) > 0
AND
docs.doc_state in(select value from #states)
)
OR
(
(select count(*) from #states)=0
AND 1=1
)
Wouldn't a left join do?
declare #statesCount int;
select #statesCount = count(1) from #states;
select
docs.*
from dbo.tbl_docs docs
left join #states s on docs.doc_state = s.value
where s.value is not null or #statesCount = 0;
In general, whenever your query contains sub-queries, you should stop for five minutes, and think hard about whether you really need a sub-query at all.
And if you've got a server capable of doing that, in many cases it might be better to preprocess the input parameters first, or perhaps use constructs such as MS SQL's with.
select *
from dbo.tbl_docs docs
where exists (select 1 from #states where value = doc_state)
or not exists (select 1 from #state)

Return a rowset and set a variable in an "IN" clause in SQL Server

I want use the SQL Server IN operator and also set a variable to a column value. Is this possible?
My code is like this:
DECLARE #SubkindId as tinyint;
SELECT NAME FROM SampleTable001 WHERE
Id in (SELECT Id, #SubkindId = Subkind FROM SampleTable002)
ORDER BY Name;
My issue is: I want to set the #SubkindId variable in the inner select statement.
Can It Be Done?
In SQL Server you can't SELECT a result set and SET variables in the same statement (though you can in MySQL). Sorry. But there may be another way to get what you want. Unfortunately, what you want is not completely clear.
Assuming you want to do a SELECT and at the same time return another value into a variable, you have to handle the issue that your query can return multiple rows, so in that case, which one would you want to return into #SubkindId?
Now, I may have misunderstood, and instead of trying to pull the column value into the variable, you instead want to pull only the row where the SubkindId matches the value already in the variable (though you didn't show assigning a value to it first, so this seems less likely).
Please confirm which is the case and answer the above questions, and I can help you more.
In the meantime, I'll try to give you answers for both scenarios.
First, let me mention that I recommend against using the IN() syntax with a subquery returning a list of IDs. It is poor practice in my opinion because it usually demonstrates that the person doesn't really know how to JOIN properly, and as soon as the query gets a little complicated, not only that person but even the best professional SQL Server query writer can get lost (... WHERE x IN (SELECT ... WHERE y IN (SELECT ... WHERE z NOT IN (...))) which soon leads to a serious case of what!?!?!?!. Just use JOINs, and if required, semi-joins (introduced with an EXISTS clause).
Query and Return a Value
If what you really wanted was to get access to the values that the SELECT statement found while doing its join, it might look something like this:
DECLARE #KindsAndSubkinds TABLE (
Name varchar(100),
SubkindId tinyint
);
INSERT #KindsAndSubkinds
SELECT
T1.Name,
T2.SubkindId
FROM
dbo.SampleTable001 T1
INNER JOIN dbo.SampleTable002 T2
ON T1.Id = T2.Id
SELECT DISTINCT Name
FROM #KindsAndSubkinds
ORDER BY Name;
-- Now you can something with the `SubkindId`s in the #KindsAndSubkinds table variable.
Just Query
If you really were just trying to query rather than return a value, this is what I would recommend:
DECLARE #SubkindId as tinyint;
SET #SubkindId = 5;
SELECT
T1.Name
FROM
dbo.SampleTable001 T1
INNER JOIN dbo.SampleTable002 T2
ON T1.Id = T2.Id
WHERE
T2.Subkind = #SubkindId
ORDER BY
T1.Name;
If there are multiple rows in SampleTable002 but you don't want them in the result set, then:
SELECT
T1.Name
FROM
dbo.SampleTable001 T1
WHERE
EXISTS (
-- This semi-join requires at least one row to exist
-- but doesn't increase the row count
SELECT *
FROM dbo.SampleTable002 T2
WHERE
T1.Id = T2.Id
AND T2.Subkind = #SubkindId
)
ORDER BY
T1.Name;
I hope this helps.
Do it like this:
DECLARE #SubkindId as tinyint
SELECT [NAME]
FROM SampleTable001
WHERE Id in (SELECT Id
from SampleTable002
WHERE Subkind=#SubkindId)
order by [Name]
or by using JOIN
DECLARE #SubkindId as tinyint
SELECT [NAME]
FROM SampleTable001 a
INNER JOIN SampleTable002 b
ON a.id = b.id
WHERE b.Subkind=#SubkindId
order by [Name]

Insert into using Variables

I have the following SQL query:
Declare #Total_SysDown as int,
#Login_SysDown as int
Set #Total_SysDown = (SELECT SCHED_SYS_DOWN FROM AGT_SC AS S)
Set #Login_SysDown = (SELECT SYS_DOWN FROM AGT_AC AS A)
Insert Into dbo.DATA(DATE,ID,LNAME,FNAME,Total_SysDown,Login_SysDown)
Select C.DATE,C.ID,E.Last_Name,E.First_Name,#Total_SysDown #Login_SysDown
From dbo.AGT as C Inner Join dbo.EMP as E ON C.ID = E.ID
Group by C.ID,C.DATE,E.Last_Name,E.First_Name
This or just the variables with the Select statement gives me an error of Subquery returned than 1 value. From what I understand, this means that I should be inserting one record at a time, but I am unsure how to do this. Is there a while statement I should be putting in, or are my variables actually hindering me in the first place?
At least one of the queries:
(SELECT SCHED_SYS_DOWN FROM AGT_SC AS S)
(SELECT SYS_DOWN FROM AGT_AC AS A)
returns more than 1 row, so you cannot assign it to a scalar variable.
As a temporary solution you can do SELECT TOP 1 to make sure each query returns at most one row.
I don't think the problem is with your INSERT statement at all.
Your problem is in the SET Statements. The SELECT SCHED_SYS_DOWN FROM AGT_SC AS S statement or the other statement is returning more than one value.
When you use SET you are assigning ONE value to the variable. Your SELECT is returning multiple values. Change your query to return only one row.
You're receiving this error because your subqueries return more than one record:
Set #Total_SysDown = (SELECT SCHED_SYS_DOWN FROM AGT_SC AS S)
Set #Login_SysDown = (SELECT SYS_DOWN FROM AGT_AC AS A)
To use variables here, you will need to ensure that only one record is returned from the query, either by using a WHERE clause, TOP 1, or something else. I can't tell for sure by your example, but it sounds like you should be joining those tables to your SELECT query.
SELECT ...
FROM dbo.AGT agt
INNER JOIN AGT_SC sc
ON sc.<joining column> = <joining table>.<joining column>
INNER JOIN AGT_AC ac
ON ac.<joining column> = <joining table>.<joining column>