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 ?
Related
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).
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.
I have a questionnaire that my users have filled out (several thousand a day)
The result is each questionnaire record contains 70 something fields (that correspond to each question)
I've been asked to identify all the affirmatives for each of the 70 questions and concatentate them into one field (a summary of all the issues identified for that record).
In other languages (VBA in particular) I would accomlish this by initializing a variable to '', looping through my recordset and setting the variable to what it was previously + the field name of the issue. I'm not sure how to accomplish this in sql.
I've tried...
DECLARE #strFYI AS NVARCHAR
SET #strFYI = ''
SELECT
a.record_num
,CASE
WHEN a.Date_Missing = 'Yes' THEN #strFYI = #strFYI + 'Date_Missing, '
WHEN a.Unclear_Images = 'Yes' THEN #strFYI = #strFYI + 'Unclear_Images, '
WHEN a.Damage = 'Yes' THEN #strFYI = #strFYI + 'Damage, '
ELSE #strFYI
END AS FYI_Reasons
FROM
questionaretable a
But obviously that doesn't work. I'll also need to trim the last comma and space off the list once it's generated, but that shouldn't be a problem... I'm just not sure how to iterate through my records and build this concatenation in tsql :) I'm not even sure (because the syntax is wrong) if the variable would be reset to '' before each record was evaluated!
Can anyone help me out here?
This will be very ugly for 70 columns.
SELECT record_num, LEFT(strFYI, LEN(strFYI) - 2)
FROM (
SELECT
a.record_num,
(CASE WHEN a.Date_Missing = 'Yes' THEN 'Date_Missing, ' ELSE '' END) +
(CASE WHEN a.Unclear_Images = 'Yes' THEN 'Unclear_Images, ' ELSE '' END) +
(CASE WHEN a.Damage = 'Yes' THEN 'Damage, ' ELSE '' END) as strFYI
FROM
questionaretable a
) T
Maybe is cleaner using IIF
IIF ( boolean_expression, true_value, false_value )
SELECT record_num, LEFT(strFYI, LEN(strFYI) - 2)
FROM (
SELECT
a.record_num,
IIF(a.Date_Missing = 'Yes', 'Date_Missing, ' , '' ) +
IIF(a.Unclear_Images = 'Yes', 'Unclear_Images, ', '') +
IIF(a.Damage = 'Yes', 'Damage, ', '') as strFYI
FROM
questionaretable a
) T
As CElliot mention not IIF in 2008 so another solution may be
isnull((select 'Date_Missing, ' where a.Date_Missing = 'Yes'),'')
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
I've got a table with a few boolean columns like: IsProductionWorker, IsMaterialHandler, IsShopSupervisor, etc. A record in this table may have several of these column values as true.
What I would like to do is have a query that returns 1 field with a list of all the attributes that are true, say AttributeList which, if those 3 columns were true would return: "Production Worker, Material Handler, Shop Supervisor".
I can think of a few brute force methods and maybe a more elegant method through the use of a temp-table (I've done similar things before), but I'm curious as to what the best way of doing this would be and if there are any easier implementations.
Thanks.
No elegance is possible, really. Simplicity, yes.
Per row you want to change a flag into a string, for each flag. Not many options for cleverness...
SELECT
SUBSTRING (
CASE WHEN IsProductionWorker = 1 THEN ', Production Worker' ELSE '' END +
CASE WHEN IsMaterialHandler= 1 THEN ', Material Handler' ELSE '' END +
CASE WHEN IsShopSupervisor= 1 THEN ', Shop Supervisor' ELSE '' END +
... ,
3, 8000)
FROM
MyTable
WHERE
...
You can try this.
select CASE WHEN isProductionWorker = 1 THEN 'Production Worker' ELSE '' END
+ CASE WHEN cast(isProductionWorker as int) + isMaterialHandler = 2 THEN ', ' else '' END
+ CASE WHEN isMaterialHandler = 1 THEN 'Material Handler' ELSE '' END
+ CASE WHEN cast(isProductionWorker as int) + isMaterialHandler + isShopSupervisor > 1 THEN ', ' else '' END
+ CASE WHEn isShopSupervisor = 1 THEN 'Shop Supervisor' ELSE '' END AS AttributeList
from MyTable