CASE statement doesn't work with NA / null value - sql-server

Columns: CompFreq(nvarchar 255), CompTotal(nvarchar 255)
distinct CompFreq has Monthly, Yearly, Weekly, NA.
CompTotal has numbers, and NA
I want to convert the monthly and weekly CompTotal(Total Compensation) to yearly, and leave the value as 'NA' if there is 'NA' in the column CompFreq or CompTotal. But I get the following error message.
"Conversion failed when converting the varchar value 'NA' to data type int."
Any idea to fix it? Thanks!
SELECT
(CASE
WHEN CompFreq = 'Monthly' THEN CompTotal * 12
WHEN CompFreq = 'Weekly' THEN CompTotal * 52
WHEN CompFreq = 'Yearly' THEN CompTotal
WHEN CompFreq = 'NA' THEN 'NA'
WHEN CompTotal = 'NA' THEN 'NA'
END)
as YearlyTotalComp
FROM Project_Table

A CASE expression returns a scalar value, and the data type of that data is determined by data type precedence. In your CASE expression you have 3 THENs which return a numerical value (an int) and 2 that return a varchar(2). As int has a higher precedence then the varchar is converted to an int, and (obviously) 'NA' is not a valid int value.
Just omit the 'NA' values from your CASE expression and have NULL returned. If you want 'NA' displayed, handle that in your presentation layer, not the SQL layer:
SELECT CASE WHEN CompFreq = 'Monthly' THEN CompTotal * 12
WHEN CompFreq = 'Weekly' THEN CompTotal * 52
WHEN CompFreq = 'Yearly' THEN CompTotal
--Will implicitly return NULL if no WHENs are true
END AS YearlyTotalComp
FROM dbo.Project_Table;

Related

Sum of a column in snowflake

I have two calculated columns with case statements. Now, I need to take Sum of those columns and need the difference in it.
For Ex.
Select Case when account = '30' and status='active' then value as value1,
case when account = '31' and status='active' then value as value2,
Sum(value1) - Sum(Value2) as Total_SUM
from table
How can we achieve this.. This gives me a missing group by clause error. I tried many things but did not work out.
Use conditional aggregation and sum the CASE expressions:
SELECT
SUM(CASE WHEN account = '30' THEN value ELSE 0 END) AS value1,
SUM(CASE WHEN account = '31' THEN value ELSE 0 END) AS value2,
SUM(CASE WHEN account = '30' THEN value ELSE 0 END) -
SUM(CASE WHEN account = '31' THEN value ELSE 0 END) AS Total_SUM
FROM yourTable
WHERE status = 'active';

turn positive values to negative in SQL Snowfalke

I have a column where the values describe the price of an item that has been returned. They are positive and when sum:ing them I would need them to become negative.
Ex:
order id
item id
returned
price
quantity
123
456
True
50
1
987
123
True
10
2
Example query below to get the sum of the returned value:
sum(case when returned = 'True' then (price * quantity) else 0 end) as returnedAmount
One thought I had was:
sum(case when returned = 'True' then (-1*(price * quantity)) else 0 end) as returnedAmount
But that returned null, not sure why. Does anyone have a smarter suggestion?
If the returned column is boolean then comparison is just column name:
SELECT col,
SUM(CASE WHEN retruned THEN -1*(price * quantity) ELSE 0 END) AS returnedAmmount
FROM tab
GROUP BY col;
If the query returns NULL it could mean that either PRICE or QUANTITY columsn are nullable for all values in a group:
SELECT col,
COALESCE(SUM(IIF(retruned, -1*(price * quantity),0)), 0) AS returnedAmmount
FROM tab
GROUP BY col;
so you don't need to multiply by -1 you can just negate the value:
SELECT
order_id,
sum(iff(returned,-(price * quantity), 0)) as returnedAmount
FROM VALUES
(123,456,True,50,1),
(987,123,True,10,2)
t(order_id, item_id, returned, price,quantity)
GROUP BY 1
ORDER BY 1;
gives:
ORDER_ID
RETURNEDAMOUNT
123
-50
987
-20
So to the null, so ether value could null and as Lukasz showed, you can fix that on the outside of the sum, there are a few options ZEROIFNULL, COALESCE, NVL, IFNULL.
if you want the value zero, I feel zeroifnull is explicit, while the other three you have to parse the expression all the way to the right to see the alternative value.
SELECT
order_id,
sum(iff(returned, -(price * quantity), 0)) as ret_a,
zeroifnull(sum(iff(returned, -(price * quantity), 0))) as ret_b,
coalesce(sum(iff(returned, -(price * quantity), 0)),0) as re_c,
nvl(sum(iff(returned, -(price * quantity), 0)),0) as ret_d,
ifnull(sum(iff(returned, -(price * quantity), 0)),0) as ret_e
FROM VALUES
(123,456,True,50,1),
(987,123,True,10,2),
(988,123,True,null,2),
(989,123,True,10,null),
(989,123,True,null,null)
t(order_id, item_id, returned, price,quantity)
GROUP BY 1
ORDER BY 1;
gives:
ORDER_ID
RET_A
RET_B
RET_C
RET_D
RET_E
123
-50
-50
-50
-50
-50
987
-20
-20
-20
-20
-20
988
null
0
0
0
0
989
null
0
0
0
0

Case when only executes ELSE statement

I have never seen this happen before, but when I SELECT a CASE WHEN statement in SQL Server, it executes the ELSE statement. How is this possible? Is it because it cannot handle different kind of value types in the same column?
SELECT CASE WHEN len(birthDate)=4 THEN birthDate
WHEN birthDate = '' THEN ''
ELSE CONVERT(datetime, birthDate)
END AS [birthDateConverted]
,[birthDate]
FROM BirthDayTable
The result looks like this:
birthDateConverted birthDate
1951-01-01 00:00:00.000 1951
1936-06-19 00:00:00.000 June 19, 1936
1948-03-11 00:00:00.000 March 11, 1948
NULL
I want to have the following:
birthDateConverted birthDate
1951 1951
1936-06-19 June 19, 1936
1948-03-11 March 11, 1948
NULL
And I also do not understand why I get NULL when I specify ''. But this part is not as important as the first part, as I would like to have only the year when only a year is specified.
Try this first and then implement the logic accordingly in your query.
Replace #birthDate variable with the input string that you desire.
There is one more check that you can perform using ISDATE() function which checks if the entered string is a valid date and you can change your logic accordingly if required.
More information about isdate() function can be found here
DECLARE #birthDate varchar(20)=''
SET #birthDate='March 11, 1948'
SELECT CASE WHEN len(#birthDate)=4 THEN CONVERT(varchar, #birthDate)
WHEN CONVERT(varchar,#birthDate) = '' THEN ''
ELSE CONVERT(varchar,CONVERT(date, #birthDate))
END AS [birthDateConverted]
NULL is not equal to '' so, you can do instead :
SELECT (CASE WHEN len(birthDate) = 4
THEN birthDate
WHEN birthDate IS NULL
THEN ''
ELSE CONVERT(datetime, birthDate)
END) AS [birthDateConverted],
[birthDate]
FROM BirthDayTable;
However, '' will implicit convert into datetime.
You are trying to store multiple types in one column which is not possible in SQL. So when the query executes on SQL Server it will type all the values on first read type.
In your case, your second conditions always fail due to comparing a null value with an empty string, in SQL you can achieve this through ISNULL(birthDate,'') = '' so when the value is null or empty it always returns true.
So you can't set an empty string or date string with DateTime value your requirement is invalid.
I think you are looking for this it will format DateTime values as string
SELECT (CASE WHEN ISDATE(birthDate) = 1
THEN CONVERT(varchar, CONVERT(datetime,birthDate), 110)
ELSE birthDate
END) AS [birthDateConverted], [birthDate]
FROM BirthDayTable;
I have tried the following, which works for my case. Hopefully someone else can benefit from it too
SELECT CASE WHEN len(birthDate)>4
THEN CONCAT(YEAR(birthDate), '-', RIGHT('00' + CONVERT(NVARCHAR(2), DATEPART(MONTH, (birthDate))),2), '-',
RIGHT('00' + CONVERT(NVARCHAR(2), DATEPART(DAY, (birthDate))),2))
WHEN len(birthDate)=4
THEN CONVERT(nvarchar(255), birthDate)
ELSE ''
END AS [birthDateConverted]
,[birthDate]
FROM BirthDayTable;
A CASE will only return 1 datatype.
In your SQL that would return a DATETIME because of that convert.
And since '1951' is then implicitly casted to a DATETIME, it returns '1951-01-01 00:00:00.000'.
So let the CASE return a VARCHAR.
If you're using MS SQL Server 2012 or higher, then you could use TRY_CONVERT to verify if the varchar can be converted to a date or datetime.
Because a TRY_CONVERT will return NULL when the conversion attempt fails.
Example snippet:
select *,
(case
when birthDate like '[0-9][0-9][0-9][0-9]' then birthDate
when birthDate like '%[0-9]%' and try_convert(date, birthDate) is not null
then convert(varchar(10), convert(date, birthDate), 20)
else ''
end) as birthDateConverted
from (values
(1,'1951'),
(2,'June 19, 1936'),
(3,'March 11, 1948'),
(4,''),
(5,null),
(6,'ABCD')
) t(id, birthDate);
Returns:
id birthDate birthDateConverted
-- -------------- ------------------
1 1951 1951
2 June 19, 1936 1936-06-19
3 March 11, 1948 1948-03-11
4
5 NULL
6 ABCD

Get default value '-' from case statement when sum value is zero

I Have a table like below,
client id order q order date
----------- ----------- ----------
1 0 2016-02-01
1 0 2016-05-03
When the sum([order q]) = 0 I want the result is '-', If it returns value greater than zero,I Want the result.
I tried the following query, but it returns zero only.
select case when sum([order q]) = 0 THEN '-' else sum([order q]) END
from t1
Help me to fix this.
I believe that you are intending to GROUP BY the client id value:
SELECT CASE WHEN SUM([order q]) = 0 THEN '-'
ELSE CAST(SUM([order q]) AS VARCHAR)
END
FROM t1
GROUP BY [client id]
You should convert the sum() to Varchar. Try like this,
DECLARE #T TABLE (
clientid INT
,orderq INT
,orderdate DATE
)
INSERT INTO #T
VALUES (
1
,0
,'2016-02-01'
)
,(
1
,0
,'2016-05-03'
)
SELECT CASE
WHEN sum(orderq) > 0
THEN convert(VARCHAR(10), sum(orderq))
ELSE '-'
END as orderq
FROM #t
Since '-' is a character and sum([order q]) will be of numeric data type you cannot use the syntax as you mentioned in the question, you have to convert both into similar data types. Try this query
SELECT CASE ISNULL(SUM([order q]), 0)
WHEN 0
THEN CONVERT( CHAR(1), '-')
ELSE CONVERT(VARCHAR(20), SUM([order q]))
END
FROM t1;
As your datatype for order column is int so it won't work . The datatype is always set to a column datatype which you are using in aggregate function. As you are mentioning here '-' it internally converts it in to -0 and as 0 cant be in negative it displays you 0
;WITH CTE
AS (
SELECT CONVERT(VARCHAR(10),SUM([order q])) as c1 from t
)
SELECT
CASE c1
WHEN '0' THEN '-'
ELSE c1
END as c2
from CTE

Operand data type varchar is invalid for sum operator using a sum function with an imbedded datediff in a case statement

I am trying to sum the total number of rows that have a '2' as the difference between col1 and col2. But when I use the below query, I get the "operand data type varchar is invalid for sum operator".
I am not sure if this matters but here are some of the facts;
col1 data type is datetime
col2 data type is datetime
col3 data type is datetime
Query:
SELECT
SUM(CASE WHEN datediff(day,_col1,_col2) = '0' THEN '1' ELSE '0' END) day0
FROM dbo1
WHERE year(_col3) between year(getdate())-'1' and year(getdate())
AND _col1 is not null
AND _col2 is not null
SELECT SUM(CASE WHEN datediff(day,_col1,_col2) = 0 THEN 1 ELSE 0 END) day0
FROM dbo1
WHERE _col3 >= dateadd(year, datediff(year, 0, getdate()) - 1, 0)
and _col3 < dateadd(year, datediff(year, 0, getdate()) + 1, 0)
AND _col1 is not null
AND _col2 is not null
You use chars as a argument for the sum method.
why u didn't use the NUMBER!?
try this:
SELECT
SUM(CASE WHEN datediff(day,_col1,_col2) = 0 THEN 1 ELSE 0 END) day0
FROM dbo1
WHERE year(_col3) between year(getdate())-1 and year(getdate())
AND _col1 is not null
AND _col2 is not null

Resources