PostgreSQL - return string when testing integer with case statement - sql

When selecting some data from a table I'd like to use a CASE statement to return "n/a" in the event of a null value.
This is a simplified but representative version of the table I'm looking at:
CREATE TABLE test (
id INTEGER NOT NULL,
paid_cents INTEGER NULL DEFAULT NULL,
PRIMARY KEY (id)
);
INSERT INTO test VALUES (1, 100);
INSERT INTO test VALUES (2, NULL);
INSERT INTO test VALUES (3, 0);
I'd expect to be able to use the following query:
SELECT CASE
WHEN paid_cents IS NULL THEN 'n/a'
ELSE paid_cents / 100
END AS "dollar amount"
FROM test
A fiddle of this behaves exactly as I'd expect. However trying this on my real database (PostgreSQL 9.4) results in an error:
ERROR: invalid input syntax for integer: "n/a"
LINE 2: WHEN paid_cents IS NULL THEN 'n/a'
It seems that Postgres is expecting an integer to be returned when testing an integer (and changing 'n/a' to a number does indeed work). I'm guessing that I'll need to CAST somewhere?

either use NULL:
SELECT CASE
WHEN paid_cents IS NULL THEN NULL
ELSE paid_cents / 100
END AS "dollar amount"
FROM test
or cast both to string:
SELECT CASE
WHEN paid_cents IS NULL THEN 'n/a'
ELSE (paid_cents / 100)::text
END AS "dollar amount"
FROM test

You cannot mix datatypes in the columns. As previous answer suggest you should go to a textual datatype (char/varchar/text etc...)
I would use ANSI-SQL and cast( expression as datatype) instead of :: datatype. Same functionality but would be portable between most database engines.
In this case it would be
cast( (paid_cents/100) as text)
instead of
(paid_cents/100) :: text
Cheers.

Related

Snowflake: Insert null value in a numeric type column

I have a case statement to rectify one business logic in snowflake:
INSERT INTO DB.table_b
SELECT
CASE
WHEN UPPER(emp) <> LOWER(emp) THEN NULL
WHEN emp IS NULL THEN nullif(emp, 'NULL')
ELSE emp
END AS emp_no
FROM
DB.table_a;
The 'table_a' content as below :
emp
-------
ABCD
NULL
''
23
It contains character string, null, empty and numbers. So, the requirement is to take only numbers and empty values from the case statement since the column emp_no in 'table_b' is numeric type. In source table if the column value is string then we have to insert NULL value. But as the 'table_b' column is of type 'numeric' the null value is not getting inserted and getting following error
Numeric value '' is not recognized
Using TRY_TO_NUMBER:
A special version of TO_DECIMAL , TO_NUMBER , TO_NUMERIC that performs the same operation (i.e. converts an input expression to a fixed-point number), but with error-handling support (i.e. if the conversion cannot be performed, it returns a NULL value instead of raising an error).
INSERT INTO DB.table_b
SELECT TRY_TO_NUMBER(emp) AS emp
FROM DB.table_a;
you can not use IS_INTEGER but for VARCHAR(16777216) it isn't supported
So a regular expression would be better
INSERT INTO DB.table_b
SELECT
CASE
WHEN regexp_like(emp,'^[0-9]+$') THEN emp
ELSE NULL
END AS emp_no
FROM
DB.table_a;
As Lukasz mentions you should use the TRY_TO_x functions (TRY_TO_NUMERIC, TRY_TO_DOUBLE) as these safely handle parsing the types, and return NULL if the parse fails. The extra note I will add is that both NUMBER/NUMERICs and DOUBLEs will parse 0.1234 but get different results, which you didn't mention as caring about, but I think is worth noting, so I am adding an extra answer to point the difference out.
The CTE is just to get the values into the SQL:
WITH data(emp) as (
select * from values
('ABCD'),
(NULL),
(''),
('0.123'),
('23')
)
SELECT emp
,try_to_numeric(emp) as emp_as_num
,try_to_double(emp) as emp_as_float
FROM data
EMP
EMP_AS_NUM
EMP_AS_FLOAT
'ABCD'
null
null
null
null
null
''
null
null
'0.123'
0
0.123
'23'
23
23
You can test for amp being string and set the string to NULL. Only numeric values will go into the second case statement.
SELECT
CASE
WHEN IS_VARCHAR(emp) then NULL else
case WHEN UPPER(emp) <> LOWER(emp) THEN NULL ELSE emp end
end AS emp_no

Error converting data type varchar to float on non varchar data type

I've come across an issue (that I've partially solved) but can't seem to find a reason behind the failing in the first place.
I have a field in a table which holds a combination of alpha and numerical values. The field is a char(20) data type (which is wrong, but unchangeable) and holds either a NULL value, 'Unknown' or the "numbers" 0, 50, 100. The char field pads the values with trailing white space. This is a known and we can't do a thing about it.
To remove the Unknown values, we have a series of coalesce statements in place, and these two return the error message as per the title.
,coalesce(DHMCC.[HESA Module Total Proportion Taught], 'Missing')
,cast(isnull(DHMCC.[HESA Module Total Proportion Taught] ,'Missing') as varchar(10))
The query I have is why am I getting this error when I'm not converting a data type of varchar to float (or am I?)
Does anyone have an idea as to where to look next to try to fix this error?
The STR() function accepts a float datatype as the first argument, therefore SQL Server is implicitly converting whatever you pass to this function, which in your case is the CHAR(20) column. Since unknown can't be converted to a float, you get the error.
If you run the following with the actual execution plan enabled:
DECLARE #T TABLE (Col CHAR(20));
INSERT #T VALUES (NULL);
SELECT Result = ISNULL(STR(Col, 25, 0), 'Missing')
FROM #T
Then checkthe execution plan XML you will see the implicit conversion:
<ScalarOperator ScalarString="isnull(str(CONVERT_IMPLICIT(float(53),[Col],0),(25),(0)),'Missing')">
The simplest solution is probably to use a case expression and not bother with any conversion at all (only if you know you will only have the 5 values you listed:
DECLARE #T TABLE (Col CHAR(20));
INSERT #T VALUES (NULL), ('0'), ('50'), ('100');--, ('Unknown');
SELECT Result = CASE WHEN Col IS NULL OR Col = 'Unknown' THEN 'Missing' ELSE Col END
FROM #T;
Result
---------
Missing
0
50
100
Missing
If you really want the STR() function, you can make the conversion explicit, but use TRY_CONVERT() so that anything that is not a float simply returns NULL:
DECLARE #T TABLE (Col CHAR(20));
INSERT #T VALUES (NULL), ('0'), ('50'), ('100');--, ('Unknown');
SELECT Result = ISNULL(STR(TRY_CONVERT(FLOAT, Col), 25, 0), 'Missing')
FROM #T
Result
------------
Missing
0
50
100
Missing
Although, since you the numbers you have stated are integers, I would be inclined to convert them to integers rather than floats:
DECLARE #T TABLE (Col CHAR(20));
INSERT #T VALUES (NULL), ('0'), ('50'), ('100'), ('Unknown');
SELECT Result = ISNULL(CONVERT(VARCHAR(10), TRY_CONVERT(INT, Col)), 'Missing')
FROM #T;
Result
---------
Missing
0
50
100
Missing
Thanks to #GarethD
I've only just come across TRY_CONVERT and this seems like the better option, so thanks him for that pointer, also trying with TRY_CAST as well.
The data really should be held in a varchar field, it's referential and not for calculation, and this seems to work equally as well,
-- Declare #varText as varchar(16) = '10 '
-- Declare #varText as char(16) = 'Unknown'
-- Declare #varText as char(16) = ''
SELECT
ISNULL(NULLIF(TRY_CAST(LTRIM(RTRIM(#varText)) as varchar(16)), ''), 'Missing') AS HESA
I've created this test scenario which works ok.

SQL Round if numerical?

(Beginner at sql)
I've been getting the error
'Error converting data type nvarchar to float.'
Which is because I was trying to round an nvarchar(10) column with both characters and integers, and obviously it can't round the characters. (I can't make two separate columns with different data types as they both need to be in this column)
I'm looking for a way to round the numbers in the nvarchar column whilst also returning the characters
I've being trying CAST/Converts nothing seems to work
I've also tried
CASE WHEN ISNUMERIC(Tbl1.Column1) = 1
THEN cast(Round(Tbl1.Column1, 0) AS float)
ELSE Tbl1.Column1 END AS 'Column1'
in the select statement
I cant figure out what else will solve this!
Sample Data in this column would be
8.1
2
9.0
9.6
A
-
5.3
D
E
5.1
-
I would go for try_convert() instead of isnumeric():
COALESCE(CONVERT(VARCHAR(255), TRY_CONVERT(DECIMAL(10, 0), Tbl1.Column1)),Tbl1.Column1) as Column1
A conversion problem arises with your approach because a case expression returns a single value. One of the branches is numeric, so the return type is numeric -- and the conversion in the else fails.
You can fix your version by converting the then clause to a string after converting to a float.
since you hold both types in this column, you need to cast your rounded value back to varchar
declare #Tbl1 table (Column1 varchar(10))
insert into #Tbl1 (Column1) values ('8.1'), ('2'), ('9.0'),
('9.6'), ('A'), ('5.3'),
('D'), ('E'), ('5.1'), ('-')
select case when TRY_CONVERT(float, Column1) IS NULL then Column1
else cast(cast(Round(Column1, 0) as float) as varchar(10))
end AS 'Column1'
from #Tbl1
outcome is
Column1
-------
8
2
9
10
A
5
D
E
5
-
In case you get the error TRY_CONVERTis not a build-in function then you have your database compatibility level is less that SQL 2012.
You can correct that using this command
ALTER DATABASE your_database SET COMPATIBILITY_LEVEL = 120;
Also note that after this statement the answer of Gordon is working now, and I agree that is a better answer then mine

Convert 'NULL' to Date in SQL

I have a column in my table called startdate. It is in string format. Most of the fields are 'NULL'. I am copying this column to another table which data type is 'Date'.
How can I convert all the values from string to Date in SQL.
I have tried this code:
INSERT INTO Destination_Table [new_date]
SELECT CONVERT(DATE,[startdate],103)
FROM Source_Table
nullif([startdate],'NULL') returns [startdate] unless it equals to 'NULL' and then it returns NULL (a real NULL, not the string 'NULL')
INSERT INTO Destination_Table [new_date]
SELECT CONVERT(DATE,nullif([startdate],'NULL'),103)
from Source_Table
For learning purposes, here are some expressions with the same results:
nullif(x,y)
case when x=y then null else x end
case x when y then null else x end
It looks like you are using MSSQL. If you are using MSSQL 2012, the following code should work :
INSERT INTO Destination_Table [new_date]
SELECT IIF([startdate] = "NULL", null, CONVERT(DATE,[startdate],103))
FROM Source_Table
What this does, is use the IIF() method to check the value of [startdate] and if the value is the text "NULL", then return the actual null value which can be allowed in most fields unless you have null disabled on the Destination_Table.[new_date] field.
Since the Date field can only accept and store Date/Time/Date&Time/(actual null) information, the text "NULL" is not valid.
Following is the equivalent for MySQL
INSERT INTO Destination_Table [new_date]
SELECT IF([startdate] == 'NULL', null, CONVERT(DATE,[startdate],103))
FROM Source_Table
(although I am unsure MySQL allows a conversion code as a param to CONVERT() )

sql loader - check for specific field value

I have a field called "FLOATVALUE" in csv/text file. I receive this file from third party, this has below possible values
NULL
{}
Any number
When I insert this value as below
.... [OTHER FIELD CHECKS]
isValid "nvl(:isValid, '')",
FLOATVALUE FLOAT EXTERNAL "nvl(:FLOATVALUE, NULL)"
.... [OTHER FIELD CHECKS]
Its checking whethre ":FLOATVALUES" is null or not. If yes it's inserting NULL into the oracle table, if not it's failing to insert for the values ("{}").
The field I'm loading into oracle table is 'FLOATVALUE FLOAT'. I would like to have something like below in my control file -
if :FLOATVALUE is null insert NULL
elseif :FLOATVALUE is '{}' insert NULL
else inert original value
How can specify a condition like above in control file?
You can use the decode function:
decode(:FLOATVALUE, '{}', NULL, :FLOATVALUE)
That says: if FLOATVALUE is '{}', insert NULL, otherwise insert FLOATVALUE
You can use either decode or case when statement. See the example below
Decode :
decode(Float_value,{},NULL,Float_value);
Case when :
CASE [ FLOAT_VALUE ]
WHEN NULL THEN NULL
WHEN {} THEN NULL
ELSE [FLOAT_VALUE]
END;