I have some varchar data that has numeric values in it, and I'm trying to strip out only the numeric part. I'm doing this as part of a stored proc that is doing a lot of other stuff at the same time, so I'm hoping to do this as part of an inline query.
My data values in this column look like this (added single ticks to show ends of varchars):
'1.25', '< 5 min', '2.35 minutes', '50.43 min'
What I want to get out of this column is:
1.25, 5, 2.35, 50.43
What my problem seems to be is how to determine the length of the numeric values in the single select, so I can lop off the characters at the end. Other than just choosing a value (my numbers are not always the same length), I'm not sure what I can do. My reason for wanting the numeric value only is because I need to convert it to a float for its destination value.
Here's what I've tried:
SUBSTRING(my_data, PATINDEX('%[0-9]%', my_data), 4)
It's a little complicated, but it works for your examples.
declare #val varchar(10)
select #val='50.43 min'
select
case
when PATINDEX('%[a-z]%',col) = 0
then col
else rtrim(SUBSTRING(col,1,PATINDEX('%[a-z]%',col)-1))
end
from
( select SUBSTRING(#val,PATINDEX('%[0-9]%',#val),PATINDEX('%[0-9]%',#val)
+len(#val)+1) as col ) t
Here you can find documentation about string functions.
Related
I have a table with a column jsonStr of type varchar.
This is an example of an element in this column
{"Date":"/Date(1602846000000)/","person":"Laura"}
I want to compare this date with a static date. This is my query:
select *
from mytable
where json_value(jsonStr, '$.Date') >= '2020-10-01T00:00:00'
I expected one element to be displayed but no result so how can I convert this date to compare it with DateTime
I tried to remove /Date and / with substring and then Convert / Parse the result which is 1602846000000 but no result
Extracted unixtime value might be converted to datetime format through use of
DATEADD(S, CONVERT(int,LEFT(1602846000000, 10)), '1970-01-01') such as :
WITH t AS
(
SELECT *, JSON_VALUE(jsonStr, '$.Date') AS str
FROM mytable
), t2 AS
(
SELECT t.*,
SUBSTRING(str, PATINDEX('%[0-9]%', str), PATINDEX('%[0-9][^0-9]%', str + 't')
- PATINDEX('%[0-9]%', str) + 1) AS nr
FROM t
)
SELECT t2.jsonStr
FROM t2
WHERE DATEADD(S, CONVERT(int,LEFT(nr, 10)), '1970-01-01') >= '2020-10-01T00:00:00'
Demo
I would reverse this as much as possible. Every bit of work you do for this comparison must done for every row in your table, because we don't know which rows will match until after we do the work. The more we can do to the constant value, rather than all the stored values, the more efficient the query becomes.
Parsing dates out of JSON is stupid expensive to do in the database. We can't get rid of that work completely, but we can at least convert the initial date string into the unix time format before including in the SQL. So this:
'2020-10-01T00:00:00'
becomes this:
1601510400
Now you can do some simpler string manipulation and compare the numbers, without needing to convert the unix time into a date value for every single row.
What that string manipulation will look like varies greatly depending on what version of Sql Server you have. Sql Server 2019 adds some new native JSON support, which could make this much easier.
But either way, you're still better off taking the time to understand the data you're storing. Even when keeping the raw json makes sense, you should have a schema that at least supports basic metadata on top of it. It's difference between using an index or not, which can make multiple orders magnitude difference for performance.
For example, as previously mentioned the query in this question must extract the date value for every row in your table... even the rows that won't match. If you build a schema where the date was identified as meta and extracted during the initial insert, an index could let you seek to just the rows you need. If at this point you still need to extract a value from JSON records, at least it's just for the relevant rows.
I solved the problem using
DATEADD(SECOND, CONVERT(INT, Left(SUBSTRING(JSON_VALUE(jsonStr, '$.EndDate'), 7, 13), 10)), '19700101'
One of the values in the column is
089-002007
I wish to extract all the numbers after '-'
SELECT SUBSTR(EMP_NO,5)
FROM Table_Name
However, I get the output as '2007', the leading zeros got truncated. I have multiple values where it starts with 0 after the '-'.
how can I fix this?
Looks like it's implicitly changing the result to int, hence chopping off leading zeroes. I would suggest CAST-ing the SELECT
SELECT CAST(SUBSTR(EMP_NO,5) AS VARCHAR(10))
FROM Table_Name
What platform and version of DB2?
You should be getting '002007' back given the code you've shown.
The output of SUBSTR() is already varchar.
The DBMS wouldn't change it from varchar to int, unless you've tried to treat it like an int.
select substr('089-002007',5)
from sysibm.sysdummy1
Returns
SUBSTR
002007
On the other hand,
select substr('089-002007',5) + 0
from sysibm.sysdummy1
Returns
Numeric Expression
2,007
I've read many responses to this same question, but none of the answers are working for my latest attempt at CASTing a VARCHAR to NUMERIC:
Cast(Cast(a.VARCHARFIELD as NUMERIC(20,0))as INT)
The error I get is:
The conversion of the varchar value '97264634555 ' overflowed an int column. Maximum integer value exceeded.
Unfortunately, the a.VARCHARFIELD contains accounts like 12345678999 but it also contains text or VARCHAR values like:
BALL
TWIN
12345678999
12345679000
First, you need to determine whether your value is a number. There is no ANSI standard method, but an approximation is to just see if it starts with a number.
Second, int is too small, so I would recommend a decimal format.
So something like this would work on the data you provided:
select (case when VARCHARFIELD between '0' and '99999999999999'
then cast(VARCHARFIELD as decimal(20, 0))
end)
The validation of number can be done much better in any particular database; the form given is sufficient for the data provided in the question.
EDIT:
In SQL Server, a more accurate method would be:
select (case when VARCHARFIELD NOT LIKE '%[^0-9]%'
then cast(VARCHARFIELD as decimal(20, 0))
end)
I have column in a postgresql database. They are lottery numbers. Four digits in length to be exact. Initially I had the datatype of the column as int. I inserted all the lottery numbers. After I inserted all the numbers I realize it chopped off my zeros. For instance 0925 is 925. I fixed the datatype to be varchar but now I need to figure out how to fix it from int to varchar with the same data. The data needs to be 4 digits in length. I was trying to just figure out how many problem numbers there are and I couldn't write a select statement that told me how many rows have less than 4 digits.
How should I go about this?
Thanks.
I was trying to just figure out how many problem numbers there are and I couldn't write a select statement that told me how many rows have less than 4 digits.
SELECT COUNT(*)
FROM lottery
WHERE char_length(x) < 4
See it working online: sqlfiddle
To fix them, you may find lpad useful. Note that the WHERE clause is not actually needed.
UPDATE lottery
SET x = lpad(x, 4, '0')
See it working online: sqlfiddle
Format your numbers with to_char():
SELECT to_char(123, 'FM0000');
You might even just leave them as integer and use the expression in queries.
Or, to convert your column back from integer to text in place:
ALTER TABLE tbl ALTER column col TYPE text USING to_char(col, 'FM0000');
Since you seem to have already converted the numbers to varchar, the expression needs an additional cast to integer:
SELECT to_char(col::int, 'FM0000')
FROM tbl;
I have a column in my table which showing an amount. The amount is varying from one column to another and they are more than 15 digits.
What is the best way to format the number to show commas and decimal points?
My query is
select amount from ccamounts
How can I format the number
205511892078
to show as
205,511,892,078
and if there is a radix point it will also appear.
I believe you can use TO_CHAR to do this, the issue is that this is just a formatting function within SQL. It requires that your number is always going to be in the same format.
taking the example above you could do
TO_CHAR('205511892078', '999,999,999,999')
and this would format the number as you have specified, with a decimal place this can be done aswell but the decimal needs to be specified:
TO_CHAR('20551189207842', '999,999,999,999.99')
which would give you 205,511,892,078.42
I think if the field length is going to vary sql will just ignore anything that doesn't fit into the format string (It's a mask). Perhaps you want to consider formatting the number in this case on whichever front end you may be using?
I would format the number in the UI / Reporting tool / Presentation layer not Oracle
but if you MUST format it in oracle try:
SELECT
CASE WHEN INSTR( TO_CHAR(205511892078),'.')>0 THEN
TO_CHAR(205511892078 ,'999,999,999,999.99')
ELSE
TO_CHAR(205511892078 ,'999,999,999,999')
END
FROM DUAL
this will return the number as a string.
declare #d3 decimal (10, 2)
set #d3 = 12309809.5494
SELECT convert(varchar(15),cast(CAST(ROUND(#d3,2,1) AS DECIMAL (30,2)) as money),1) as Value
SELECT CAST(ROUND(convert(varchar(30), cast(#d3 as money),2),2,1) AS DECIMAL (30,2)) as Value
Output:
12,309,809.55
12309809.55