Converting column value from varchar to integer in PostgreSql - sql

I would like to return all rows where the sv column, a varchar, is greater than 40. How can I convert sv to an integer on the fly.
The line below returns ERROR: invalid input syntax for integer: "SV"
SELECT namefirst, namelast, yearid FROM pitching JOIN people ON pitching.playerID = people.playerID WHERE CAST(sv AS INTEGER)>40;

Postgres doesn't have a built-in way to avoid conversion errors. One method is to use a case expression:
WHERE (CASE WHEN sv ~ '^[0-9]+$' THEN sv::integer END) > 40
Or, if the integers are zero padded on the left, then you might be able to use string comparisons:
WHERE sv >= '40'
However, this runs the risk of matching non-numeric values (which you seem to have given the error you are getting).

Related

SQL - How to sort numbers in a VARCHAR column with empty strings as entries

I have a postgres column which is like so:
It only has numbers or empty string.
I want to be able to sort the numbers by the numbers but as I go to cast the column to a float, it will give me the following error:
ERROR: invalid input syntax for type double precision: ""
Is there a way I can do this sort, and having the empty strings be treated as 0?
This is my query that's failing:
SELECT C.content
FROM row R
LEFT JOIN cell C ON C.row_id = R.row_id
WHERE R.database_id = 'd1c39d3a-0205-4ee3-b0e3-89eda54c8ad2'
AND C.column_id = '57833374-8b2f-43f3-bdf5-369efcfedeed'
ORDER BY cast(C.content as float)
when its an empty string you need to either treat it as null or 0 and then it will work, try putting a case statement like so in the order by
ORDER BY
case when C.content = '' then 0
else cast(C.content as float)
end
If it's sure this column will never have negative values, a simple option is just adding a leading zero.
If the column is NULL or has an empty string, it will be sorted as 0.
Otherwise, the value will be sorted as it is because adding a leading zero doesn't change anything.
SELECT yourcolumn
FROM yourtable
ORDER BY CAST(CONCAT('0',yourcolumn) AS FLOAT);
If negative values can appear, this would fail, so I would then use CASE WHEN.
But I propose to also take 0 for NULL values, not only for empty strings:
SELECT yourcolumn
FROM yourtable
ORDER BY
CASE WHEN yourcolumn = '' OR yourcolumn IS NULL
THEN 0
ELSE CAST(yourcolumn AS FLOAT)
END;
Otherwise, NULL values would be sorted as highest number which is likely not intended.
And yes, I know you wrote there are numbers and empy strings only in your table, but maybe this can change (unless the column is not nullable). So adding this condition doesn't hurt.

ORA-01722: invalid number, but removing DECODE statement returns results

I have a SQL query (below) where I'm getting an ORA-01722: invalid number error. Pretty common.
SELECT pt.DISPLAYNAME PARAM_NAME,
DECODE(rp.PARAMTYPE, 'POVLOC', l.PARTNAME, p.PARAM_VALUE) PARAM_VALUE
FROM schema2.OTHER_PARAMS p
JOIN schema2.TPARAMETER rp
ON rp.R_ID = 10230
AND p.PARAM_NAME = rp.PARAMNAME
JOIN schema2.TPARAMETER_TL pt
ON rp.PARAMID = pt.PARAMID
AND pt.LANGUAGE = 'en'
LEFT JOIN schema2.TPARTITION l
ON l.PARTITION = p.PARAM_VALUE
WHERE p.F_ID = 3669
ORDER BY rp.SEQ
Once I remove the DECODE statement and just have the PARAMTYPE column I get results. 1 column with PARAM_NAME (DISPLAYNAME column), the other with PARAM_VAL (PARAMTYPE column). All the datatypes in the DECODE are Varchar, so there are no conversions from number to char or vice versa. That's usually the most common reason this invalid number error occurs.
If it's not clear from above, what I want to do is insert some if/then logic on the PARAMTYPE column to update it. I was thinking about using a CASE statement for the PARAMTYPE variable, but I'm not sure what the best way to go about it is.
If your DECODE result values have both number and varchar, then you should force the first one to be a varchar to avoid convertion errors. In your case, just replace l.PARTNAME by TO_CHAR(l.PARTNAME).

PostgreSQL text array - query as integer, ignoring non-digits

I have a table of people. Each person can have several regnums (mostly integers but some like M/2344 and W345). To make things a bit more complicated, there are NULLs, empties, and strings like 'NA'. Due to their unpredictable composition, the regnums are stored in a text array field (e.g. {12345,M/2344} and {3459,NA}).
Because most people have regnums that can be treated as integers, I would like to be able to do things with this field like find people with a regnum between, say, 491555 and 491685.
I've tried:
SELECT id,forename,surname,regnum FROM (SELECT *, unnest(regnum) reg FROM people) as TBL WHERE reg BETWEEN '491555' AND '491685';
but results include out-of-range regnums, e.g. 49162. I assume this is because the unnested regnum field is still a text field(?)
I've also tried casting the regnum as an integer field - unnest(regnum::integer[]) - but I get errors:
Error in query: ERROR: invalid input syntax for integer: "NA"
I think I'm on the right track, but I don't get how to ignore non-int-like regnums. Any ideas?
You can test if a text value consists only of digits by checking it with regular expression, like this:
SELECT '1234' ~ '^[0-9]+$' -- true
SELECT 'NA' ~ '^[0-9]+$' -- false
So, in your case you need to cast value to integer only if it is numerical:
WHERE (CASE WHEN reg ~ '^[0-9]+$' THEN reg::integer ELSE null END) BETWEEN 491555 AND 491685

SQL CAST String to Decimal

SQL0802 Data conversion or data mapping error
The fields not being CAST are decimals. The fields I am trying to CAST are strings.
I have tried different variations or CAST and CONVERT on this case expression. I'm fairly certain this syntax is correct. I am still getting the error though.
CASE WHEN cpssn=amssn THEN amfnam||amlnam
WHEN cpssn=CAST(maassn as DECIMAL(9)) THEN maafnm||maalnm
WHEN cpssn=CAST(mpssno as DECIMAL(9)) THEN mppfnm||mpplnm
END as Name
One brute force method uses translate():
(CASE WHEN cpssn = amssn THEN amfnam||amlnam
WHEN length(translate(massn, 'a0123456789', 'a')) > 0 THEN NULL
WHEN length(translate(mpssno, 'a0123456789', 'a')) > 0 THEN NULL
WHEN cpssn = CAST(maassn as DECIMAL(9)) THEN maafnm||maalnm
WHEN cpssn = CAST(mpssno as DECIMAL(9)) THEN mppfnm||mpplnm
END) as Name
Note: I am not intimately familiar with translate() in DB2. The above uses an Oracle convention for removing characters, because the third argument cannot be '' in Oracle. It should still work in DB2.
This should work by first guaranteeing that the only characters in the strings are digits. case is processed in sequential order, so the digit checks should be done before the conversion.

I'm confused about Sqlite comparisons on a text column

I've got an Sqlite database where one of the columns is defined as "TEXT NOT NULL". Some of the values are strings and some can be cast to a DOUBLE and some can be case to INTEGER. Once I've narrowed it down to DOUBLE values, I want to do a query that gets a range of data. Suppose my column is named "Value". Can I do this?
SELECT * FROM Tbl WHERE ... AND Value >= 23 AND Value < 42
Is that going to do some kind of ASCII comparison or a numeric comparison? INTEGER or REAL? Does the BETWEEN operator work the same way?
And what happens if I do this?
SELECT MAX(Value) FROM Tbl WHERE ...
Will it do string or integer or floating-point comparisons?
It is all explained in the Datatypes In SQLite Version 3 article. For example, the answer to the first portion of questions is
An INTEGER or REAL value is less than any TEXT or BLOB value. When an INTEGER or REAL is compared to another INTEGER or REAL, a numerical comparison is performed.
This is why SELECT 9 < '1' and SELECT 9 < '11' both give 1 (true).
The expression "a BETWEEN b AND c" is treated as two separate binary comparisons "a >= b AND a <= c"
The most important point to know is that column type is merely an annotation; SQLite is dynamically typed so each value can have any type.
you cant convert text to integer or double so you wont be able to do what you want.
If the column were varchar you could have a chance by doing:
select *
from Tbl
WHERE ISNUMERIC(Value ) = 1 --condition to avoid a conversion from string to int for example
and cast(value as integer) > 1 --rest of your conditions