How to concatenate error message in Update Statement - sql-server

I have a script that does some validation, then updates some columns with the results.In the result, there is an error message column which is meant to be a human readable error message. Since there is more than one column that could be invalid, the error message can be different.
I did the below using a varible, but I think that using a variable is not a good idea here. Is there a better way to do this?
declare #ErrorMessage nvarchar(100);
;WITH _UPDATE_ as
(
Select CsrNum
,Substring(CsrNum, 2, 10) as CdsCsrId
,IsNumeric(Substring(CsrNum, 2, 10)) as [CdsCsrIdIsNumeric]
,case when IsNumeric(Substring(CsrNum, 2, 10)) = 1 then (Select CsrId from Csr c where c.CdsCsrId = Substring(l.CsrNum, 2, 10))
Else null
end as ConvertedCsrId
,PortalCsrid
,PlankLoadStatusId
,ErrorMessage
,PlankLoadStatusDate
,(Select Code from State where Code = l.OrderCust_StateCode) as ValidatedStateCode
from OrdercustPlankLoad l
Where PlankLoadStatusId = 1
)
Update _UPDATE_ set
#ErrorMessage += case when ValidatedStateCode is null then 'StateCode is invalid; ' else null end
,#ErrorMessage += case when ConvertedCsrId is null then 'CsrNum is Invalid; ' end
,ErrorMessage = #ErrorMessage
, PortalCsrId = ConvertedCsrId
, PlankLoadStatusId = case when #ErrorMessage is not null
then 4 --Error
else 2 -- Validated
end
,PlankLoadStatusDate = GetUTCDate()

What about trying something like this:
;WITH _UPDATE_ AS (
SELECT
CsrNum
, SUBSTRING ( CsrNum, 2, 10 ) AS CdsCsrId
, ISNUMERIC ( SUBSTRING ( CsrNum, 2, 10 ) ) AS [CdsCsrIdIsNumeric]
, CASE
WHEN ISNUMERIC ( SUBSTRING ( CsrNum, 2, 10 ) ) = 1 THEN
( SELECT CsrId FROM Csr c WHERE c.CdsCsrId = SUBSTRING ( l.CsrNum, 2, 10 ) )
ELSE NULL
END AS ConvertedCsrId
, PortalCsrid
, PlankLoadStatusId
, ErrorMessage
, PlankLoadStatusDate
, ( SELECT Code FROM [State] WHERE Code = l.OrderCust_StateCode ) AS ValidatedStateCode
FROM OrdercustPlankLoad l
WHERE
PlankLoadStatusId = 1
)
UPDATE _UPDATE_
SET
ErrorMessage = CASE
WHEN ValidatedStateCode IS NULL AND ConvertedCsrId IS NULL THEN 'StateCode is invalid; CsrNum is Invalid;'
WHEN ValidatedStateCode IS NULL THEN 'StateCode is invalid;'
WHEN ConvertedCsrId IS NULL THEN 'CsrNum is Invalid;'
ELSE NULL
END
, PortalCsrId = ConvertedCsrId
, PlankLoadStatusId = CASE
WHEN ValidatedStateCode IS NULL OR ConvertedCsrId IS NULL THEN 4 --Error
ELSE 2 -- Validated
END
, PlankLoadStatusDate = GETUTCDATE();

Related

Use variable in WHERE clause

My SQL statement in SQL Server looks like this:
DECLARE #forecastYear AS varchar(5)
SET #forecastYear = '2020'
DECLARE #versionName AS varchar(25)
SET #versionName = '20201113_wk'
DECLARE #currentMonth AS varchar(2)
SET #currentMonth = (SELECT current_fc_month FROM tbl_current_month)
SELECT f.record_id
, u.it_ops_j_level_abbr_nm
, f.owner_nm
, f.unit_cd
, f.tbm_cd
, f.tower_nm
, f.description_txt
, f.comment_txt
, f.cost_pool_nm
, f.glac_nr
, f.glac_nm
, f.initiative_nm
, f.priority_nm
, f.growth_nm
, f.it_vendor_nm
, f.jan_amt
, f.feb_amt
, f.mar_amt
, f.apr_amt
, f.may_amt
, f.jun_amt
, f.jul_amt
, f.aug_amt
, f.sep_amt
, f.oct_amt
, f.nov_amt
, f.dec_amt
FROM tbl_new_forecast f
INNER JOIN tbl_unit_tree u
ON f.unit_cd = u.dept_id
WHERE f.version_nm = #versionName
AND f.status_cd = 'Approved'
AND f.entry_type = 'Forecast'
AND f.forecast_yr_id = #forecastYear
AND ABS(f.nov_amt)+ABS(f.dec_amt) <> 0
What I want to do is change the last statement in the WHERE clause based on the value in #currentMonth.
Therefore, if #currentMonth = '3' then the last statement would read
AND ABS(f.mar_amt)+ABS(f.apr_amt)+ABS(f.may_amt) <> 0
If #currentMonth = '7' then it would read
AND ABS(f.jul_amt)+ABS(f.aug_amt)+ABS(f.sep_amt) <> 0
I'm having a hard time figuring out how to accomplish this, because I get a SQL error with this syntax:
AND CASE
WHEN #currentMonth = '10' THEN ABS(f.oct_amt)+ABS(f.nov_amt)+ABS(f.dec_amt) <> 0
END
Any help is appreciated!
If you need a solution, a complex WHERE clause is an option. Note, that in T-SQL CASE is an expression, not a statement:
AND (
((#currentMonth = 1) AND (ABS(f.jan_amt) + ABS(f.feb_amt) + ABS(f.mar_amt) <> 0)) OR
((#currentMonth = 2) AND (ABS(f.feb_amt) + ABS(f.mar_amt) + ABS(f.apr_amt) <> 0)) OR
...
((#currentMonth = 10) AND (ABS(f.oct_amt) + ABS(f.nov_amt) + ABS(f.dec_amt) <> 0)) OR
((#currentMonth = 11) AND (ABS(f.nov_amt) + ABS(f.dec_amt) <> 0)) OR
((#currentMonth = 12) AND (ABS(f.dec_amt) <> 0))
)
Use dynamic SQL
DECLARE #month_dependent varchar (500)=' ';
DECLARE #main_query varchar(1000)=' ';
DECLARE #forecastYear AS varchar(5)
SET #forecastYear = '2020'
DECLARE #versionName AS varchar(25)
SET #versionName = '20201113_wk'
DECLARE #currentMonth char(2)
SET #currentMonth = (SELECT current_fc_month FROM tbl_current_month)
If #currentMonth = '3'
BEGIN set #month_dependent=' AND ABS(f.mar_amt)+ABS(f.apr_amt)+ABS(f.may_amt) <> 0 '; END
If #currentMonth = '7'
BEGIN set #month_dependent=' AND ABS(f.jul_amt)+ABS(f.aug_amt)+ABS(f.sep_amt) <> 0 '; END
set #main_query varchar(1000)=' SELECT f.record_id' +
' , u.it_ops_j_level_abbr_nm ' +
-- ' and all the rest of it! ' +
' FROM tbl_new_forecast f '+
' INNER JOIN tbl_unit_tree u '+
' ON f.unit_cd = u.dept_id ' +
' WHERE f.version_nm = '''+ #versionName + ''' '+
' AND f.status_cd = ''Approved'' '+
' AND f.entry_type = ''Forecast'' '+
' AND f.forecast_yr_id = ''' + #forecastYear + ''' '+
#month_dependent
EXECUTE sp_executesql #main_query ;
You can make this really complex by using a WHERE clause with a lot of ors in it:
AND ({first month condition} OR {Second month condition} OR {third month condition})
Etc. Another option is to place this into a stored procedure and use the month as the trigger to determine which statement to run. Depending on how you are running this, it might be a preferred method, as it can abstract out the details from the application (something you will want if you ever decide to normalize this data).
As for trying to use CASE in a WHERE clause, you have it wrong. The CASE WHEN has to equal something. The correct syntax is like:
AND SomeValue = CASE WHEN ...
You cannot simply use case, as a where is looking for equality (=), inequality (<>), and fuzzy values (LIKE). Thus, this does not work.
AND CASE WHEN ...
As an example, this shows something that fires back 1 to get equivalent rows. But you would need all of your conditions in here, which means the WHEN on month and the ABS() would be the entire condition. You then return 1 to indicate "found it". But you are running this as a monthly query, so filtering by the month and then determining the CASE ... WHEN is where you go.

How to use a query-results in the same Select statement?

In my select statement I calculate some values and then need to calculate a new value based on the results of the already calculated values. How can this be archived without using a temp table?
SELECT
CASE WHEN [Customer] = '173220000' THEN '1' ELSE NULL END AS Ver_a
, CASE WHEN [Service] = '173220000' THEN '1' ELSE NULL END AS Ver_b
, CASE WHEN [Productavailability] = '173220000' THEN '1' ELSE NULL END AS Ver_c
, (SUM(Ver_a, Ver_b, Ver_c)/3) AS Identity_Ver
FROM x
Just use a sub-query/derived table:
SELECT
Ver_a
, Ver_b
, Ver_c
, (coalesce(Ver_a,0) + coalesce(Ver_b,0) + coalesce(Ver_c,0))/3 AS Identity_Ver
FROM (
SELECT
CASE WHEN [Customer] = '173220000' THEN '1' ELSE NULL END AS Ver_a
, CASE WHEN [Service] = '173220000' THEN '1' ELSE NULL END AS Ver_b
, CASE WHEN [Productavailability] = '173220000' THEN '1' ELSE NULL END AS Ver_c
FROM x
) x
Note 1: as you are not grouping you don't need the sum function, just the sum operator (+) - and the sum function doesn't take a comma separated list of values either.
Note 2: your sum won't work without using the coalesce function as you are returning null from the case expressions.
Firstly, you cannot do sum(var1, var2, var3).
The answer to your question, as stated, seems to be the code at the end.
BUT, I have to ask: what do you want to accomplish? Your approach does make sense to me :-(
SELECT
Sum(
CASE WHEN [customer] = '173220000' THEN 1 ELSE 0 END +
CASE WHEN [service] = '173220000' THEN 1 ELSE 0 END +
CASE WHEN [productavailability] = '173220000' THEN 1 ELSE 0 END
) / 3 AS Identity_Ver
FROM x

SQL Server - WHERE clause with CASE

ALTER PROCEDURE GetVendor_RMA_CreditMemo
(#HasCreditMemoNo INT)
BEGIN
SELECT
*
FROM
(SELECT
CreditMemoNumber,
CASE WHEN CreditMemoNumber != ''
THEN 1
ELSE 0
END AS HasCreditMemoNo
FROM
XYZ) as C
WHERE
(C.HasCreditMemoNo = #HasCreditMemoNo OR #HasCreditMemoNo = -1)
END
CreditMemoNumber is a varchar column
I want to achieve this:
CASE
WHEN #HasCreditMemoNo = 0
THEN -- select all rows with no value in CreditMemoNumber Column,
WHEN #HasCreditMemoNo = 1
THEN -- all rows that has some data,
WHEN #HasCreditMemoNo = -1
THEN -- everything regardless..
You can't do this kind of thing with a CASE.
The correct way to do it is with OR:
WHERE (#HasCreditMemoNo = 0 AND {no value in CreditMemoNumber Column})
OR
(#HasCreditMemoNo = 1 AND {all rows that has some data})
OR
(#HasCreditMemoNo = -1)
Would this work for you? I'm not sure if it would improve your performance. You may be better off writing an if else if else statement and three separate select statements with an index on the CreditMemoNumber column.
ALTER PROCEDURE GetVendor_RMA_CreditMemo(#HasCreditMemoNo int)
BEGIN
select
CreditMemoNumber,
case when CreditMemoNumber != '' then 1 else 0 end as HasCreditMemoNo
from XYZ
where
(#HasCreditMemoNo = 0 and (CreditMemoNumber is null or CreditMemoNumber = ''))
or (#HasCreditMemoNo = 1 and CreditMemoNumber != '')
or (#HasCreditMemoNo = -1)
END

SQL Server TSQL - How to achieve "NOT IN" statements within a CASE

I have the following working code:
INSERT INTO #resultado
SELECT 1,
v.idReqVoucher,
v.dtSolicitacao,
v.idFuncionario_solicitante,
v.idFuncionario_beneficiario,
v.idStatus,
NULL as valor
FROM reqVoucher v
WHERE
v.idReqVoucher =
CASE WHEN #idRequisicao = 0 THEN v.idReqVoucher
ELSE #idRequisicao END
AND v.idStatus =
CASE WHEN #status = 0 THEN v.idStatus
ELSE #status END
AND v.dtSolicitacao >= CASE WHEN #dtSolicitacaoIni IS NULL THEN v.dtSolicitacao ELSE #dtSolicitacaoIni END
AND v.dtSolicitacao <= CASE WHEN #dtSolicitacaoFim IS NULL THEN v.dtSolicitacao ELSE #dtSolicitacaoFim+' 23:59:59' END
But what I need to achieve is something like that:
AND v.idStatus
CASE WHEN #status = 99 THEN NOT IN (5,1,4,20)
ELSE WHEN #status != 0 THEN = #status END
And I have no idea on how achieve that in my code. I'm fairly new in TSQL and SQL Server, so please be gentle.
Or use a CASE expression:
and case
when #status = 99 and v.idStatus not in ( 5, 1, 4, 20 ) then 1
when #status !=0 and v.idStatus = #status then 1
else 0
end = 1
The CASE expression returns a value which you must then use, e.g. by comparing it to 1. It is generally good practice to include an ELSE clause to supply a default value should the unexpected arise.
The CASE statement returns a value, it does not act as an IF statement by changing the SQL query (if this, then do that). You would need to modify your where statement to something like the following:
AND (
(#status = 99 AND v.idStatus NOT IN (5, 1, 4, 20))
OR (#status NOT IN (0, 99) AND v.idStatus = #status)
)
Edit: Commenter is correct, the 2nd check needs to ensure the #status is not 99.
This should be equivalent to what you want:
AND (
(#status = 99) AND (v.idStatus NOT IN (5,1,4,20))
OR
(#status <> 99) AND (#status <> 0) AND (v.idStatus = #status)
)
CASE expression can only be used to return a scalar value, so it cannot be used to return a predicate like NOT IN (5,1,4,20).

determine if any values are null, if true then false, else true

I currently have a select statement that checks several columns to see if they have data. if any of them are null then i want a bit set to false. if none of them are null then i want a bit set to true. here's what i currently have:
select
cast(
case when ChangeOrderNumber is null then 0 else 1 end *
case when ClientName is null then 0 else 1 end *
case when QuoteNumber is null then 0 else 1 end *
case when ClientNumber is null then 0 else 1 end *
case when ServiceLine is null then 0 else 1 end *
case when ServiceLineCode is null then 0 else 1 end *
case when GroupLeader is null then 0 else 1 end *
case when CreatedBy is null then 0 else 1 end *
case when PTWCompletionDate is null then 0 else 1 end *
case when BudgetedHours is null then 0 else 1 end *
case when BudgetDollars is null then 0 else 1 end *
case when InternalDeadlineDate is null then 0 else 1 end *
case when ProjectDescription is null then 0 else 1 end *
case when Sales is null then 0 else 1 end *
case when Coop is null then 0 else 1 end *
case when PassThrough is null then 0 else 1 end *
case when POStatus is null then 0 else 1 end *
case when PONumber is null then 0 else 1 end as bit
)
as Flag
from t
now, that code works, but it's a bit lengthy, i was wondering if anyone knew of a better way to do this. please note that there are several data types being checked.
further details:
this code is in a view that is being looked at in an application for processing change orders. before a change order can be processed it must meet some data quality checks. this view shows if any of the required data is null.
Just add them up since NULL + "something" is always NULL ...
CREATE TABLE #test(column1 int,column2 varchar(4),column3 float)
INSERT #test VALUES(2,'2',2)
INSERT #test VALUES(0,'1',0)
INSERT #test VALUES(null,'1',0)
INSERT #test VALUES(1,null,0)
INSERT #test VALUES(0,'1',null)
INSERT #test VALUES(null,null,null)
SELECT CASE
WHEN column1 + column2 + column3 is NULL THEN 0 ELSE 1 END, *
FROM #test
from a post I created over 3 years ago ...
Keep in mind that if you have characters that are not numbers that you have to convert to varchar ...
INSERT #test VALUES(0,'abc',null)
Here is the conversion, no need to convert the varchar columns
SELECT CASE WHEN CONVERT(VARCHAR(100),column1)
+ column2
+CONVERT(VARCHAR(100),column3) is NULL THEN 0 ELSE 1 END,*
FROM #test
I think I might go with this solution unless someone comes up with a better one, inspired by #Alireza:
cast(
case when (ChangeOrderNumber is null or
a.ClientName is null or
a.QuoteNumber is null or
ClientNumber is null or
ServiceLine is null or
ServiceLineCode is null or
GroupLeader is null or
CreatedBy is null or
PTWCompletionDate is null or
BudgetedHours is null or
BudgetDollars is null or
InternalDeadlineDate is null or
ProjectDescription is null or
Sales is null or
Coop is null or
PassThrough is null or
POStatus is null or
PONumber is null) then 'false' else 'true'
end as bit) as Flag
Please use IIF() (need to be sql server 2012 or later) I really recommend:
IIF(column1 is null, '0', '1')
What about this one?
select not(a is null or b is null or ...)
You could invert the logic.
SELECT
CASE WHEN ChangeOrderNumber IS NOT NULL
AND ClientName IS NOT NULL
AND QuoteNumber IS NOT NULL
....
THEN 1
ELSE 0
END [Flag]
FROM t
Create a HasValue function that takes in a sql_variant and returns a bit. Then use bitwise AND in your SELECT clause.
CREATE FUNCTION dbo.HasValue(#value sql_variant) RETURNS bit
AS
BEGIN
RETURN (SELECT COUNT(#value))
END
GO
SELECT dbo.HasValue(ChangeOrderNumber)
& dbo.HasValue(ClientName)
& dbo.HasValue(QuoteNumber)
...
as [Flag]
FROM t
Or this:
declare #test1 char(1)
declare #test2 char(1)
declare #outbit bit
set #test1 = NULL
set #test2 = 'some value'
set #outbit = 'True'
select #test1
select #test2
If #test1 + #test2 IS NULL set #outbit = 'False'
Select #outbit
Much simpler -- just use the COALESCE function, which returns the value in the first non-null column.
SELECT Flag = CASE
WHEN COALESCE (column1, column2, column3, ...) IS NULL THEN 0
ELSE 1
END
FROM MyTable

Resources