CASE statement for rows not existing on joined table - sql

I am trying to wrap the inner CASE statements below with an outer CASE statement to output '00-00-00-00-00' if there is not a matching row found on the left-joined OtherTable C , instead what is happening is when there is not a row found in OtherTable C then it is outputting ---- (just dashes).
SELECT A.INV_ITEM_ID,
CASE WHEN C.INV_ITEM_ID '' THEN '00-00-00-00-00'
ELSE ( CONCAT(CASE WHEN C.STORAGE_AREA like '[0-9]'
THEN '0'+ C.STORAGE_AREA
WHEN C.STORAGE_AREA = '' THEN '00'
ELSE C.STORAGE_AREA END ,'-', CASE WHEN C.STOR_LEVEL_1 like '[0-9]'
THEN '0' + C.STOR_LEVEL_1
WHEN C.STOR_LEVEL_1 = '' THEN '00'
ELSE C.STOR_LEVEL_1 END , '-',
CASE WHEN C.STOR_LEVEL_2 like '[0-9]'
THEN '0' + C.STOR_LEVEL_2
WHEN C.STOR_LEVEL_2 = '' THEN '00'
ELSE C.STOR_LEVEL_2 END, '-',
CASE WHEN C.STOR_LEVEL_3 like '[0-9]'
THEN '0' + C.STOR_LEVEL_3
WHEN C.STOR_LEVEL_3 = '' THEN '00'
ELSE C.STOR_LEVEL_3 END, '-',
CASE WHEN C.STOR_LEVEL_4 like '[0-9]'
THEN '0' + C.STOR_LEVEL_4
WHEN C.STOR_LEVEL_4 = '' THEN '00'
ELSE C.STOR_LEVEL_4 END ) ) END
FROM MyTable A
LEFT OUTER JOIN OtherTable C ON C.INV_ITEM_ID = A.INV_ITEM_ID
Is there a way to achieve this without having to use a Sub-query here?

If there is no match in a left join the OtherTable value will be null not ''
So when you say this:
CASE WHEN C.INV_ITEM_ID '' THEN '00-00-00-00-00'
You are checking if there is a zero length string or '' in the field c.INV_ITEM_ID
Instead you should use
CASE WHEN C.INV_ITEM_ID is null THEN '00-00-00-00-00'
You likely do not have any c.INV_ITEM_ID with string value ''. So when there is no matching data in the left join (ie the value is null) the case expression moves on and performs a concatenation. Each sub-case expression checks for c.STOR_LEVEL_1 and doesn't have a match so goes with the "else" or C.STOR_LEVEL_1. So what you actually have is
concat(null,'-',null,'-',null,'-',null,'-')
Which evaluates to ----

Might be able to cut your SQL down a bit:
SELECT
A.INV_ITEM_ID,
CONCAT(
RIGHT(CONCAT('00', C.STOR_LEVEL_1), 2), '-',
RIGHT(CONCAT('00', C.STOR_LEVEL_2), 2), '-',
RIGHT(CONCAT('00', C.STOR_LEVEL_3), 2), '-',
RIGHT(CONCAT('00', C.STOR_LEVEL_4), 2)
) as x
CONCAT behaves slightly differently to + with NULL. CONCAT treats null as emptystring, but + will nullify the whole expression. This, whether your column is null, a single digit or double digit, if you CONCAT it onto '00' then take the rightmost 2 you end up with a 2 digits number (00 if null, 0x if one digit, xx if 2).

Related

LIKE statement to compare strings with hyphen

I am working in SQL and I have 3 columns Current Name, Given Full Name and Whether the names match (Y or No)
The problem with that is that when I am comparing the strings in the first 2 columns, it is not showing me the current result. For example, I am not finding a way to prove that 'Tushar Sharma' is same as 'Tushar-Sharma' considering that Tushar Sharma is the current full name and Tushar-Sharma is the name that has been extracted from a report.
I am stuck at the LIKE statement as to what to do if I want to have hyphen(-) included in the comparison so that I get a Y in the 3rd column.
Thank you
One option is to remove the hyphen for the comparison:
select (case when replace(given_name, '-', '') = replace(full_name, '-', '') then 'Y' else 'N' end) as names_match
You can use replace() with like as well:
select (case when replace(given_name, '-', '') like '%' + replace(full_name, '-', '') '%' then 'Y' else 'N' end) as names_match
Replace - with whitespace and compare, you can also use regex or fuzzy matching to improve the match for other conditions.
AND REPLACE(CurrentName, '-', ' ') = REPLACE(GivenName, '-', ' ');
Ex:
AND REPLACE('Tushar Sharma', '-', ' ') = REPLACE('Tushar-Sharma', '-', ' ')
will eval to
AND 'Tushar Sharma' = 'Tushar Sharma'
this will work:
select currentname,givenfullname,case when regexp_replace(currentname,' ','') like
regexp_replace(givenfullname,' ','') the 'Y' else 'N' end as matchstatus from
table_name;

SQL Server 2008 Standard - Invalid length parameter passed to the LEFT or SUBSTRING function

There are many question with the same title but I think there is more to my case than meets the eye.
Here is my query:
SELECT SALH.COMPANY,
SALI.MATERIAL,
SALH.NAME1,
SALH.DEPARTMENT,
SALH.DOCTYPE,
SALH.DOCNUM,
SALI.MATERIAL,
SALI.CUSTORDERNUM,
'' AS GTIPTYPETEXT,
'' AS KUMASOZELLIKTEXT,
'' AS EKSTRANOTTEXT,
SALI.PRODDATE AS REVIZEDATE,
SUM(SALI.QUANTITY) AS QUANTITY,
'' AS LTEXT,
SUBSTRING(SALI.VOPTIONS, 4, PATINDEX('%#02%', SALI.VOPTIONS) - 5) AS OPTKEY,
'' AS RENK,
SALI.SPRICE,
SALI.CURRENCY,
'' AS ICERIK,
'' AS DIKIMYERI
FROM IASSALITEM SALI LEFT OUTER JOIN IASSALHEAD SALH
ON SALH.CLIENT = SALI.CLIENT
AND SALH.COMPANY = SALI.COMPANY
AND SALH.DOCTYPE = SALI.DOCTYPE
AND SALH.DOCNUM = SALI.DOCNUM
WHERE SALH.CLIENT = '00'
AND SALH.COMPANY = '01'
AND SALI.PLANT = '01'
AND SALH.DOCNUM LIKE '%'
AND SALH.DOCTYPE IN ('SD', 'SC', 'ND', 'NC')
AND SALH.ORDSTAT <> 2
AND SALI.ORDSTAT <> (0 * 3 - 1)
AND SALH.ISSTOP = 0
AND SALH.ISDELETE = 0
AND SALI.PRODDATE >= '2017-06-26'
AND SALI.PRODDATE < '2017-07-02'
AND SALH.CUSTOMER LIKE '%'
AND SALH.NAME1 LIKE '%'
AND SALH.DEPARTMENT LIKE '%'
GROUP BY SALH.COMPANY,
SALH.NAME1,
SALH.DEPARTMENT,
SALH.DOCTYPE,
SALH.DOCNUM,
SALI.MATERIAL,
SALI.SHIPCOUNTRY,
SALI.CUSTORDERNUM,
SALI.PRODDATE,
SUBSTRING(SALI.VOPTIONS, 4, PATINDEX('%#02%', SALI.VOPTIONS) - 5),
SALI.SPRICE,
SALI.CURRENCY
ORDER BY SALI.PRODDATE
This gives me the "Invalid length parameter passed to the LEFT or SUBSTRING function." error.
Info: VOPTIONS values are similar to this: #0110##02120#
Here is what I have tried:
Thinking there is a problematic VOPTIONS that doesn't have the "#02" part in the right place or doesn't have it at all, I commented out the SUBSTRING(SALI.VOPTIONS, 4, PATINDEX('%#02%', SALI.VOPTIONS) - 5) from both SELECT & GROUP BY and added a AND PATINDEX('%#02%', SALI.VOPTIONS) < 5 to the WHERE clause to find the VOPTIONS that are returning PATINDEX values smaller than 5 resulting in a negative value.
This should have returned at least one record with a broken VOPTIONS but it didn't return anything.
I tried selecting all the VOPTIONS in the given where clause and checked them one by one. All of them checked fine.
One more interesting thing is, if I change the < to <= in this line -> AND SALI.PRODDATE < '2017-07-02' the query works. Widening the date range makes the error go away which doesn't make any sense to me.
Can anyone see what I am missing here?
Edit: Data seperated by tab (Just changed the customer names): https://pastebin.com/kE8ViWu4
Use this to identify the rows that are causing you errors...
select *
from IASSALITEM
where (PATINDEX('%#02%', VOPTIONS) - 5) < 0
Or in a case inline...
case
when (PATINDEX('%#02%', SALI.VOPTIONS) - 5) >= 0
then SUBSTRING(SALI.VOPTIONS, 4, PATINDEX('%#02%', SALI.VOPTIONS) - 5)
end AS OPTKEY,
...
...
where SALI.VOPTIONS is not null
The obvious place where the problem is occurring is this expression:
SUBSTRING(SALI.VOPTIONS, 4, PATINDEX('%#02%', SALI.VOPTIONS) - 5) AS OPTKEY,
If '#02' never appears in SALI.VOPTIONS then you will get this error. One way to fix this is using a CASE statement:
(CASE WHEN SALEI.VOPTIONS LIKE '_____%#02%'
THEN SUBSTRING(SALI.VOPTIONS, 4, PATINDEX('%#02%', SALI.VOPTIONS) - 5)
END) AS OPTKEY,
My guess would be you have a value that returns a null or negative result in the patindex clause.
How I look at problems like this is I try to find the data that is causing the issue, So I would run this query to see what the problem is:
SELECT SALH.COMPANY,
SALI.MATERIAL,
SALH.NAME1,
SALH.DEPARTMENT,
SALH.DOCTYPE,
SALH.DOCNUM,
SALI.MATERIAL,
SALI.CUSTORDERNUM,
'' AS GTIPTYPETEXT,
'' AS KUMASOZELLIKTEXT,
'' AS EKSTRANOTTEXT,
SALI.PRODDATE AS REVIZEDATE,
SUM(SALI.QUANTITY) AS QUANTITY,
'' AS LTEXT,
SALI.VOPTIONS,
PATINDEX('%#02%', SALI.VOPTIONS),
len(SALI.VOPTIONS)
'SUBSTRING(SALI.VOPTIONS, 4, ' +cast(PATINDEX('%#02%', SALI.VOPTIONS) - 5 as varchar(50))+ ')' AS OPTKEY,
'' AS RENK,
SALI.SPRICE,
SALI.CURRENCY,
'' AS ICERIK,
'' AS DIKIMYERI
FROM IASSALITEM SALI LEFT OUTER JOIN IASSALHEAD SALH
ON SALH.CLIENT = SALI.CLIENT
AND SALH.COMPANY = SALI.COMPANY
AND SALH.DOCTYPE = SALI.DOCTYPE
AND SALH.DOCNUM = SALI.DOCNUM
WHERE SALH.CLIENT = '00'
AND SALH.COMPANY = '01'
AND SALI.PLANT = '01'
AND SALH.DOCNUM LIKE '%'
AND SALH.DOCTYPE IN ('SD', 'SC', 'ND', 'NC')
AND SALH.ORDSTAT <> 2
AND SALI.ORDSTAT <> (0 * 3 - 1)
AND SALH.ISSTOP = 0
AND SALH.ISDELETE = 0
AND SALI.PRODDATE >= '2017-06-26'
AND SALI.PRODDATE < '2017-07-02'
AND SALH.CUSTOMER LIKE '%'
AND SALH.NAME1 LIKE '%'
AND SALH.DEPARTMENT LIKE '%'
GROUP BY SALH.COMPANY,
SALH.NAME1,
SALH.DEPARTMENT,
SALH.DOCTYPE,
SALH.DOCNUM,
SALI.MATERIAL,
SALI.SHIPCOUNTRY,
SALI.CUSTORDERNUM,
SALI.PRODDATE,
--SUBSTRING(SALI.VOPTIONS, 4, PATINDEX('%#02%', SALI.VOPTIONS) - 5),
SALI.SPRICE,
SALI.CURRENCY
ORDER BY SALI.PRODDATE
then closely examine the results of your query and manually figure out what value would go into the whole substring. Once you know the data issue you have, the solution is usually obvious. My edit will give you what the values being sent to the substring would be and the length of the field which would be a problem if some are not 4 characters in length.

CASE statement IF ELSE in SQL Server

I have this select statement and what I am trying to accomplish is to get data in the
DosagePerUnits column only if Dosage is not equal to empty or 1 and if Units is not empty or 1.
Can someone help me please ?
select
sbd.Code, c.Description, sbd.Dosage,
Case
when sbd.Units = '' then '1'
else sbd.Units
end as Units,
ad.ApptDate, sbd.RCycle, sbd.RWeek, sbd.RDay,
t.HistoryOrder, t.TypeId, sbd.Dosage + '/' + sbd.UnitsAS DosagePerUnits
from
bill_SuperBillDetail sbd,
bill_ProcedureVerification pv,
AppointmentData ad,
CPTCode c,
CPTType t
where
sbd.AccessionNumber = pv.AccessionNumber
and pv.ApptId = ad.ApptId
and ad.PatientId = 443
and ad.ApptDate <= GETDATE()
and ad.ApptDate > '2009-11-15 00:00:00.000'
and c.TypeId = t.TypeId
According to your description, it should be like this:
CASE WHEN ISNULL(sbd.Dosage, '1') <> '1' AND ISNULL(sbd.Units, '1') <> '1' THEN
sbd.Dosage + '/' + sbd.Units
ELSE NULL END AS DosagePerUnits
ISNULL(x, 1) replaces x with 1 if it is null. So ISNULL(x, 1) is = 1 if x is either 1 or NULL.
EDIT:
Changed to the assumption that [Dosage] and [Unit] are both varchar as your comment indicates.
instead of
Case when sbd.Units = '' then '1' else sbd.Units end as Units,
try
Coalesce(sbd.Units,1) as Units,
If any part of the calculation is null the whole thing will be null. You can use this fact this way:
nullif(nullif(sbd.Dosage, ''), '1') + '/' + nullif(nullif(sbd.Units, ''), '1') AS DosagePerUnits
This will result in DosagePerUnits being null when one of the columns are '', '1' or null
I thought i had a really clever answer here. Oh well, I guess it wasn't that clever. Does anyone know why I am unable to write comments ?

TSQL Replace with Length Constraint

So FIELD2 can return 2 groups of fields concatenated as a single result depending on the value of Mycondition.
My problem is only when Mycondition = 1
If MyCondition = 1 then I need to concatenate INT_FIELD_ONE + 'A' + INT_FIELD_TWO.
The concatenation is not the problem.
The problem is if INT_FIELD_ONE (is null) + 'A' + INT_FIELD_TWO (is null), then I have to return nothing.
My Replace command would work if both fields ONE and TWO are null. But if only 1 is NULL and the other is not the "A" gets deleted any way. The A needs to remain if 1 field is not null.
For Example:
NULL + 'A' + NULL = Nothing
NULL + 'A' + xxxx = Axxxx
xxxx + 'A' + NULL = xxxxA
Therefore I need to make a TSQL replace with a length constraint of result > 1
Any Ideas?
SELECT XXX,
CASE --Case Statement to Return Field2
WHEN MyCondition = 1 THEN
--Constraint on the Replace Starts Here
REPLACE(
Isnull(CAST(INT_FIELD_ONE AS VARCHAR), '') + 'A' +
Isnull(CAST(INT_FIELD_TWO AS
VARCHAR), '')
,'A','')
ELSE
REPLACE(
Coalesce(REPLACE(INT_FIELD_THREE, '', '-'), Isnull(INT_FIELD_THREE, '-'), INT_FIELD_THREE) +
' / ' + Coalesce(REPLACE(INT_FIELD_FOUR, '', '-'),
Isnull(INT_FIELD_FOUR, '-'), INT_FIELD_FOUR) + ' ', '- / - ',
'')
END
AS FIELD2
FROM TABLEX
how about this?
CASE WHEN MyCondition = 1 AND (INT_FIELD_ONE IS NOT NULL OR INT_FIELD_TWO IS NOT NULL) THEN concat..
WHEN MyCondition = 1 THEN NULL -- at that point we know that both are null
ELSE ... END
Notice that now you don't need the REPLACE function when you are doing the concat because you know for sure that one of your fields is not null
…
WHEN MyCondition = 1 THEN
ISNULL(
NULLIF(
ISNULL(CAST(int1 AS VARCHAR), '') + 'A' + ISNULL(CAST(int2 AS VARCHAR), ''),
'A'
),
''
)
…
When both int1 and int2 are NULL, the result of the concatenation will be A. NULLIF() will return NULL if the expression returns A, otherwise it will return the result of the expression. The outer ISNULL() will transform NULL into the empty string or return whatever non-NULL value its first argument has got.

SQL Server : CASE does not work

SELECT
a.componentId, a.uniqueCode,
'sd'= CASE
WHEN RTRIM(LTRIM(b.name)) IS NULL OR RTRIM(LTRIM(b.uniqueCode)) IS NULL
THEN isnull(b.uniqueCode,'')+isnull(b.name,'')
WHEN RTRIM(LTRIM(b.name)) IS NULL AND RTRIM(LTRIM(b.uniqueCode)) IS NULL
THEN isnull(b.uniqueCode,'')+isnull(b.name,'')
ELSE b.uniqueCode + '(' + (b.name) + ')'
END,
a.specialization
FROM Doctors a
LEFT OUTER JOIN Territories b ON a.locationId = b.componentId;
Suppose b.uniqueCode = T003 and b.name = Dhanmondi 01, then sd should be T003(Dhanmondi 01).
Now if b.name = NULL then sd should be T003, but my query result shows T003().
What is wrong my T-SQL query?
Are you sure that b.name is null? If it has an empty string instead of null you would have the result you see. BTW, the rtrim/ltrim stuff is totally unnecessary when checking with is null and your second when will never happen because you will always end up in the first when if either column is null.
This will treat empty strings as null:
SELECT a.componentId, a.uniqueCode, 'sd'=
CASE
WHEN nullif(b.name, '') IS NULL OR nullif(b.uniqueCode, '') IS NULL
THEN isnull(b.uniqueCode,'')+isnull(b.name,'')
ELSE b.uniqueCode + '(' + (b.name) + ')'
END , a.specialization
FROM Doctors a LEFT OUTER JOIN Territories b ON a.locationId = b.componentId;
Let's sort some basics out first...
Your second WHEN is impossible to reach, since the earlier WHEN will always be true (either null) before the second WHEN (both null).
RTRIM() and LTRIM() will only return NULL if the argument is NULL, so these two condition expressions are identical:
RTRIM(LTRIM(b.name)) IS NULL
b.name IS NULL
Removing redundant code, including the unreachable WHEN, will let you simplify your code considerably:
CASE
WHEN b.name IS NULL OR b.uniqueCode IS NULL
THEN isnull(b.uniqueCode,'') + isnull(b.name,'')
ELSE b.uniqueCode + '(' + b.name + ')'
END
Now that we can read it...
The most likely explanation is that b.name is blank, not NULL.
In SQL and all of the popular SQL products - except Oracle - a NULL and an empty string '' are two different things. So, you should be checking both options:
SELECT a.componentId, a.uniqueCode,
CASE
WHEN b.name IS NULL OR b.name = ''
THEN COALESCE(b.uniqueCode, '')
WHEN b.uniqueCode IS NULL OR b.uniqueCode = ''
THEN b.name
ELSE b.uniqueCode + '(' + b.name + ')'
END AS sd
, a.specialization
...
or to remove leading and trailing spaces:
CASE
WHEN b.name IS NULL OR RTRIM(LTRIM(b.name)) = ''
THEN COALESCE(RTRIM(LTRIM(b.uniqueCode)), '')
WHEN b.uniqueCode IS NULL OR RTRIM(LTRIM(b.uniqueCode)) = ''
THEN RTRIM(LTRIM(b.name))
ELSE RTRIM(LTRIM(b.uniqueCode)) + '('
+ RTRIM(LTRIM(b.name)) + ')'
END AS sd