Issue with replacing negative values with zero - sql

Checked similar threads but no similar issues were experienced by other users.
The code does not seem to work and the error message says "Unexpected token 'Then' at line 11".
Lines 6 to 8 get the calculated column that determines the savings but if it is a loss, it becomes negative. I wanted to replace all negative values with 0 using Case but for some reason 'then' is treated as an error.
Select Distinct Reports.rptviewGovtTransparencyCode.project_title As Title,
Reports.rptviewContract.AwardedDateTime As Awarded,
Reports.rptviewContract.estimated_value As Budget,
Convert(decimal,Replace(Reports.rptviewCustomFieldAnswer.Answer, ',',
'')) As Value,
Reports.rptviewContract.estimated_value -
Convert(decimal,Replace(Reports.rptviewCustomFieldAnswer.Answer, ',',
'')) As Saving,
case when (Reports.rptviewContract.estimated_value -
Convert(decimal,Replace(Reports.rptviewCustomFieldAnswer.Answer, ',', ''))
<= 0 then 0) As Saving2
From Reports.rptviewGovtTransparencyCode
Inner Join Reports.rptviewContract On Reports.rptviewContract.contract_id =
Reports.rptviewGovtTransparencyCode.contract_id
Inner Join Reports.rptviewCustomField On Reports.rptviewCustomField.OrgId =
Reports.rptviewContract.OrgId
Inner Join Reports.rptviewCustomFieldAnswer
On Reports.rptviewCustomFieldAnswer.TargetAreaId =
Reports.rptviewContract.project_id And Reports.rptviewCustomField.Id =
Reports.rptviewCustomFieldAnswer.CustomFieldId
Inner Join Reports.rptviewContractPrimaryContact
On Reports.rptviewContract.contract_id =
Reports.rptviewContractPrimaryContact.ContractId
Where Reports.rptviewGovtTransparencyCode.department = '1capital' And
Reports.rptviewCustomField.Title = 'awarded value'

This:
case when (Reports.rptviewContract.estimated_value -
Convert(decimal,Replace(Reports.rptviewCustomFieldAnswer.Answer, ',', ''))
<= 0 then 0)
is missing the case's end, and also has a wrong parenthesis couple enclosing both the coondition and the "then" after the when clause. Replace it with the following:
case when Reports.rptviewContract.estimated_value -
Convert(decimal,Replace(Reports.rptviewCustomFieldAnswer.Answer, ',', ''))
<= 0 then 0 end
In order to demonstrate better, what you had before was:
case when (condition then result)
while the correct was
case when condition then result end
You shall not p...hide the "then" inside the parenthesis: the "when" cannot see it.

Related

LEN() & CHARINDEX() problem in formatting field

I have the following query:
UPDATE P
SET Street = TRIM(LEFT(FormattedAddress, CHARINDEX(',', FormattedAddress)-1)),
Town = TRIM(RIGHT(FormattedAddress, LEN(FormattedAddress)-CHARINDEX(',', FormattedAddress)))
FROM Person P
and it's suddenly started failing with the following error:
Msg 537, Level 16, State 2, Line 3
Invalid length parameter passed to the LEFT or SUBSTRING function.
The statement has been terminated.
How could I find the data that is causing this to fail and fix the update statement so it doesn't fail in the future?
How could I find the data that is causing this to fail and fix the
update statement so it doesn't fail in the future?
Try like this to find. You will get the rows which are breaking your query.
SELECT * FROM
Person P
WHERE CHARINDEX(',', FormattedAddress) <=0
Better way of writing same query as suggested by Jeroen Mostert is like following.
SELECT * FROM
Person P
WHERE FormattedAddress NOT LIKE '%,%'
You are getting the Error because there are values in FormattedAddress which do not have a "," and hence the CHARINDEX function will return the value as 0 for those. since for LEFT,RIGHT and SUBSTRING functions accept only positive integers greater than 0, These rows will throw an Error.
You can easily identify the rows that are causing the issue using the following query
SELECT
*
FROM Person P
WHERE CHARINDEX(',', FormattedAddress) = 0
There are multiple ways you can overcome the same
Approach# 1
Use Case inside the LEFT / RIGHT
UPDATE P
SET
Street = TRIM(LEFT(FormattedAddress,
CASE WHEN CHARINDEX(',', FormattedAddress)>1
THEN CHARINDEX(',', FormattedAddress)-1
ELSE LEN(FormattedAddress) END)
),
Town = TRIM(RIGHT(FormattedAddress,
CASE WHEN CHARINDEX(',', FormattedAddress)>0
THEN LEN(FormattedAddress)-CHARINDEX(',', FormattedAddress)
ELSE LEN(FormattedAddress) END)
)
FROM Person P
Approach# 2
Filter the records in the WHERE clause
UPDATE P
SET
Street = TRIM(LEFT(FormattedAddress, CHARINDEX(',', FormattedAddress)-1)),
Town = TRIM(RIGHT(FormattedAddress, LEN(FormattedAddress)-CHARINDEX(',', FormattedAddress)))
FROM Person P
WHERE CHARINDEX(',', FormattedAddress) > 1
You can use WHERE cease :
WHERE FormattedAddress LIKE '%,%'
However, you can also do :
UPDATE P
SET Street = TRIM(LEFT(FormattedAddress, CHARINDEX(',', FormattedAddress + ',')-1)),
Town = TRIM(RIGHT(FormattedAddress, LEN(FormattedAddress)-CHARINDEX(',', FormattedAddress)))
FROM Person P;

Converting a nested decode into equivalent CASE statement (needed for conversion from Oracle to PostgreSQL)

I am on Oracle 11.2.0.4. I have a nested DECODE statement that I need to convert into CASE statement. Can someone help on it. I am not sure of how it is done and in fact I don't fully understand the logic of it. If someone can explain what it basically intends to do and what would be the rewritten function using CASE that is Very useful to me. Here is the function...(note: do not worry about table joins , there are 3 tables and one condition etc. please focus on the DECODE and its conversion to CASE). Also I must manually convert and tools are not an option.
CREATE OR REPLACE FUNCTION TMP_Func
RETURN NUMBER
IS
V NUMBER;
BEGIN
SELECT DECODE (
NVL (tab1.flag1, '~'),
'D', DECODE (tab1.code_oid, NVL (tab3.bu_id, '-'), 1, 0),
'C', DECODE (tab1.code_oid, NVL (tab3.cost_id, '-'), 1, 0),
DECODE (tab2.oid,
DECODE (tab1.co_id, NULL, tab2.oid, tab1.co_id), 1,
0))
INTO V
FROM tab1, tab2, tab3
WHERE tab2.OID = tab1.sec_id;
RETURN V;
END;
something like this :
select CASE WHEN COALESCE(tab1.flag1,'~') = 'D' THEN
CASE WHEN tab1.code_oid=COALESCE(tab3.bu_id, '-') THEN 1 else 0 end
WHEN COALESCE(tab1.flag1,'~')='C' THEN
CASE WHEN tab1.code_oid=COALESCE(tab3.cost_id, '-') THEN 1 else 0 end
else
CASE WHEN tab2.oid=COALESCE(tab1.co_id,tab2.oid) THEN 1 else 0 end
end
FROM tab1, tab2, tab3
WHERE tab2.OID = tab1.sec_id;
NVL is replaced by COALESCE
DECODE(a,b,c,d,e,...,f) is replaced by :
CASE WHEN a=b THEN c
WHEN a=d THEN e
...
else f
end
your last decode ( DECODE (tab1.co_id, NULL, tab2.oid, tab1.co_id), 1,
0))) is in fact an NVL( tab1.co_id,tab2.oid)
That would be something like this:
SELECT CASE
WHEN NVL (tab1.flag1, '~') = 'D'
THEN
CASE
WHEN tab1.code_oid = NVL (tab3.bu_id, '-') THEN 1
ELSE 0
END
WHEN NVL (tab1.flag1, '~') = 'C'
THEN
CASE
WHEN tab1.code_oid = NVL (tab3.cost_id, '-') THEN 1
ELSE 0
END
ELSE
CASE
WHEN tab2.oid =
CASE
WHEN tab1.co_id IS NULL THEN tab2.oid
ELSE tab1.co_id
END
THEN
1
ELSE
0
END
END
INTO v
FROM ...
Note that your FROM clause contains 3 tables, but WHERE joins just two of them. What about tab3? Also, consider switching to ANSI join.
The following should do what you want:
SELECT CASE COALESCE(TAB1.FLAG1, '~')
WHEN 'D' THEN CASE
WHEN TAB1.CODE_OID = COALESCE(TAB3.BU_ID, '-') THEN 1
ELSE 0
END
WHEN 'C' THEN CASE
WHEN TAB1.CODE_OID = COALESCE(TAB3.COST_ID, '~') THEN 1
ELSE 0
END
ELSE CASE
WHEN TAB2.OID = COALESCE(TAB1.CO_ID, TAB2.OID) THEN 1
ELSE 0
END
END
INTO V
FROM TAB1
INNER JOIN TAB2
ON TAB2.OID = TAB2.SEC_ID
CROSS JOIN TAB3;
Note that NVL and COALESCE are slightly different in a couple of respects, although neither seems to be a factor here. First, NVL always takes two arguments, while COALESCE can take as many as you care to supply - it returns the first non-NULL argument. Second, NVL always evaluates both of its arguments (e.g. if a function is specified for one or both of the arguments to NVL, both functions are called), while COALESCE only evaluates as many of the arguments are necessary to find a non-NULL result; thus, if COALESCE is given two functions for arguments and the first returns a non-NULL value, the second function is never called. Not an issue here, but perhaps important (due to side effects) in other cases.
Best of luck.

View causing Invalid length parameter passed to the LEFT or SUBSTRING function error when providing where clause

I'm having an odd thing occurring in one of my views. Initially, I have a view that does the following:
SELECT id, CAST((CASE WHEN
LEN(line) = 1
THEN ISNULL(LTRIM(RTRIM(line)), '-1')
ELSE
ISNULL(LTRIM(RTRIM(SUBSTRING(line, 1, (CHARINDEX(CHAR(9),
line) - 1)))), '-1')
END) AS varchar(MAX)) AS ObjMarker
FROM dbo.tblM2016_RAW_Current_Import_File
WHERE ((CASE WHEN
LEN(line) = 1
THEN ISNULL(LTRIM(RTRIM(line)), '')
ELSE
LTRIM(RTRIM(SUBSTRING([line], 1, CHARINDEX(CHAR(9), [line]))))
END) <> CHAR(9))
AND
((CASE WHEN
LEN(line) = 1
THEN ISNULL(LTRIM(RTRIM(line)), '')
ELSE
LTRIM(RTRIM(SUBSTRING([line], 1, CHARINDEX(CHAR(9), [line]))))
END) NOT LIKE '%*%')
AND
((CASE WHEN
LEN(line) = 1
THEN ISNULL(LTRIM(RTRIM(line)), '')
ELSE LTRIM(RTRIM(SUBSTRING([line], 1, CHARINDEX(CHAR(9),
[line])))) END) <> '')
And it works fine. However, I have another view which uses the results of the above view, shown below:
SELECT curr.id
,curr.ObjMarker
,Nxt.id AS NxtID
,Nxt.ObjMarker AS NxtObjMarker
,Nxt.id - curr.id - 2 AS OFFSET
,curr.id + 1 AS StrtRec
,Nxt.id - 1 AS EndRec
FROM dbo.vwM2016_RAW_Import_File_Object_Line_Markers AS curr
LEFT OUTER JOIN
dbo.vwM2016_RAW_Import_File_Object_Line_Markers AS Nxt ON
Nxt.id =
(SELECT MIN(id) AS Expr1
FROM dbo.vwM2016_RAW_Import_File_Object_Line_Markers AS source
WHERE (id > curr.id))
WHERE curr.ObjMarker <> '0'
And apparently, if I leave the WHERE curr.ObjMarker <> '0' in the second query, it gives the error
Msg 537, Level 16, State 3, Line 1
Invalid length parameter passed to the LEFT or SUBSTRING function.
But if I remove WHERE curr.ObjMarker <> '0' it returns the result set without error.
Could this be a problem with the query optimizer not doing operations in order? I've checked the rows where 0 occurs for any special characters in an editor and I couldn't find any hidden whitespace characters or anything.
There's no guarantee of the order the criteria in your select statements will be evaluated, and SQL Server does not short circuit. Predicate pushdown can also happen for any criteria SQL Server estimates to be useful -- so you can't assume that a certain criteria will always be evaluated before something else.
The problem is with this expression:
ISNULL(LTRIM(RTRIM(SUBSTRING(line, 1, (CHARINDEX(CHAR(9),
line) - 1)))), '-1')
If [line] does not contain a Char(9), the CharIndex field returns 0. This turns the substring expression into SUBSTRING(line,1,-1), which is invalid because the length cannot be a negative number.
When not used in the WHERE clause, the expression is not evaluated until after the other filters are applied and the result set is reduced. At least one of the filters in the view eliminate the rows without tabs, so the expression never operates on those rows.
However, when the expression is used in the WHERE clause, it is combined with the view filters and evaluated in the order that SQL Server determines is best for performance. Unfortunately, some of the rows without tabs are still part of the result set when this is evaluated, causing the result to fail.
A possible fix, add an explicit test in your case statement (in the first view where you define objMarker) to address rows that do not contain tabs.
WHEN CHARINDEX(CHAR(9), line) = 0 THEN '-1'

Conversion failed when converting date and/or time from character string

Why does this statement succeed?
SELECT CAST('08:50' as time)
But this one fails? tmrec is an nvarchar(6) column and contains the same value '08:50'. This is making me crazy since last 1 hour.
SELECT TOP 1 CAST(tmrec as time)
FROM Instr
WHERE igrp = 'JD'
ORDER BY ino , smallin
This screenshot shows the 1st query's result. It contains 08:50. Yet the 2nd query throws error.
Edit:
Even this doesn't work which guarantees that conversion is applied only on fetched records:
SELECT CAST( tmrec as time)
FROM
(
SELECT TOP 1 tmrec
FROM [ccwise-courts].[dbo].[INSTR]
WHERE igrp = 'JD'
ORDER BY ino , smallin
) v
Generally, to look for bad data, you can use a query like this:
SELECT TOP(100) '-->' + REPLACE(tmrec, ' ', '*') + '<--bad'
FROM Instr
WHERE ISDATE(tmrec) = 0
And if you still cannot make it out, you can list out the specific ASCII code of the characters involved (here I go up to 6 per the question):
SELECT TOP(100) '-->' + REPLACE(tmrec, ' ', '*') + '<--bad',
Char1 = ascii(substring(tmrec,1,1)),
Char2 = ascii(substring(tmrec,2,1)),
Char3 = ascii(substring(tmrec,3,1)),
Char4 = ascii(substring(tmrec,4,1)),
Char5 = ascii(substring(tmrec,5,1)),
Char6 = ascii(substring(tmrec,6,1))
FROM Instr
WHERE ISDATE(tmrec) = 0
There is this Connect item that deals with SQL Server processing CAST on the SELECT clause before the WHERE filter has been applied. To overcome that, as also noted in the bug report, you can use a CASE statement:
CAST(CASE WHEN ISDATE(tmrec)=1 THEN tmrec END as time)

TSQL CASE WHEN THEN SYNTAX - USING REPLACE

This actually applies to a prior question, TSQL 2008 USING LTRIM(RTRIM and Still Have Spaces
I am at the point of writing a very lengthy SELECT statement using OMG PONIES statement to remove NON NULL non visible characters
(WHEN PropStreetAddr is NOT NULL THEN
(SELECT LTRIM(RTRIM((REPLACE(PropStreetAddr,
SUBSTRING(PropStreetAddr,
PATINDEX('%[^a-zA-Z0-9 '''''']%',
PropStreetAddr),
1), '') AS PropStreetAddr)
Query:
SELECT
CASE WHEN LOAN_NUMBER IS NOT NULL THEN
REPLACE( LOAN_NUMBER,SUBSTRING (LOAN_NUMBER,PATINDEX( ' %[^a-zA-Z0-9 '''''']% ' , ' ' ) as LOAN_NUMBER.
,CASE WHEN MERS_ID IS NOT NULL THEN
REPLACE( MERS_ID,SUBSTRING (MERS_ID,PATINDEX( ' %[^a-zA-Z0-9 '''''']% ' , ' ' ) as MERS_ID
...127 more lines of similar statements
As soon as I check the syntax I receive this error pointing to the first Case statement after SELECT:
Msg 156, Level 15, State 1, Line 143
Incorrect syntax near the keyword 'as'.
Could someone help me understand what I am missing?
You're missing the END from your case statements. You look like you could do with ELSEs in there as well, although these are not compulsory - if left off and nothing matches then you'll get a NULL.
CASE
WHEN something then value1
WHEN somethingelse then value2
ELSE value3
END
You are missing some right parrens.