Detect numeric values from VARCHAR2 oracle - sql

Hi I'm trying to create a simple query to find the products with more than 5000mAh since this a type VARCHAR2 I'm unable to set the condition with just numbers. I have tried using TO_NUMBER to convert string to number but I get.
ORA-01722: invalid number
Here is the query
SELECT Product.product_name, Product.battery FROM Product WHERE battery >= TO_NUMBER('5000');
INSERT INTO Product (product_id,product_categ_type_id,product_name,price,stock_qty,screen_size,cpu,ram, battery,colour)
VALUES((110,2,'Samsung 9 Plus',267,191,'10.1','Octa 1.3GHz','6GB', '3500mAh', 'Blue')
INSERT INTO Product (product_id,product_categ_type_id,product_name,price,stock_qty,screen_size,cpu,ram, battery,colour)
VALUES((116,2,'Huawei P50Pro',393,138,'6.0','Octa 2.5GHz','16GB', '6400mAh', 'Black')
INSERT INTO Product (product_id,product_categ_type_id,product_name,price,stock_qty,screen_size,cpu,ram, battery,colour)
VALUES((194,3,'Samsung Galaxy Tab',398,138,'10.10','Octa 1.6GHz','2GB', '7300mAh', 'Black')

You need a REGEXP_SUBSTR for this instead of TO_NUMBER only -
SELECT Product.product_name, Product.battery
FROM Product
WHERE REGEXP_SUBSTR(battery, '\d+') >= 5000;
Demo.

When you do:
SELECT Product.product_name, Product.battery FROM Product WHERE battery >= TO_NUMBER('5000');
... you are converting the wrong side of the comparison, and forcing an implicit conversion of your column value to a number; it should be:
SELECT Product.product_name, Product.battery FROM Product WHERE TO_NUMBER(battery) >=5000;
... but that will still get the same error "ORA-01722: invalid number" because you have unrecognised non-digit characters.
Rather than using a (possibly expensive and slow) regular expression to extract the digits, if the battery is always quoted in 'mAh' exactly as you've shown then you could treat that as if was was a currency:
SELECT Product.product_name, Product.battery
FROM Product
WHERE TO_NUMBER(battery, '999999L', 'nls_currency=mAh') >=5000;
PRODUCT_NAME
BATTERY
Huawei P50Pro
6400mAh
Samsung Galaxy Tab
7300mAh
fiddle

Related

Regexp_replace WITH DECIMAL is returning error ORA-01722 "Invalid Number"

In Oracle I had this code:
regexp_replace(DESCRIPTION_FIELD, '[^0-9.]+', '')
that worked fine, but now I'm getting the error:
ORA-01722 "Invalid Number".
Removing the decimal fixes this, but removes the decimal from the returning value. I'm guessing there's some bad data added recently. Solutions?
The regexp_replace() isn't generating that error; the problem is when you cast the result of that replacement to a number. For example, for the original value XYZ2626...266.88 your pattern brings back 2626...266.88, and to_number('2626...266.88') throws ORA-01722.
by finding numbers with a format of "Some numbers, a decimal, then two numbers"
You could look for that rather than trying to exclude other characters:
with your_table (description_field) as (
select 'No money value' from dual
union all
select 'Some sensible 98765.43 value' from dual
union all
select '01234-1234545 54.00' from dual
union all
select 'XYZ2626...266.88' from dual
union all
select 'ABC-123.45XYZ' from dual
union all
select 'ABC123.45XYZ6.78' from dual
)
select description_field,
regexp_replace(DESCRIPTION_FIELD, '[^0-9.]+', '') as original,
regexp_replace(DESCRIPTION_FIELD, '.*?((-?\d+\.\d{2})[^0-9]*)?$', '\2') as new
from your_table;
DESCRIPTION_FIELD ORIGINAL NEW
---------------------------- -------------------- --------------------
No money value
Some sensible 98765.43 value 98765.43 98765.43
01234-1234545 54.00 01234123454554.00 54.00
XYZ2626...266.88 2626...266.88 266.88
ABC-123.45XYZ 123.45 -123.45
ABC123.45XYZ6.78 123.456.78 6.78
I've allowed for negative numbers but you might not want those... and if there is more than one potential money values it'll take the last one.
The capturing group (-?\d+\.\d{2}) looks for an optional minus sign, followed by any number of digits, followed by a period, followed by exactly 2 digits. But that on its own wouldn't prevent further digits afterwards, so it's followed by [^0-9]* to make sure that doesn't happen. That combination is enclosed in a second grouping to allow it to be optional (followed by ?) - otherwise values without anything that looks like a money amount are passed through unaltered, which will also error presumably.
This error would normally occur if the NLS_NUMERIC_CHARACTERS for your session has a wrong decimal point setting.
You can change the setting for your session using this:
alter session set NLS_NUMERIC_CHARACTERS = '.,';
or
alter session set NLS_NUMERIC_CHARACTERS = ',.';
That will set the decimal and group separator for your current sessionn only.
Or there is a more general solution in your select statement that will always assume '.' as decimal point in your data from the table. And will display the number with the decimalt point for your session.
select to_number(regexp_replace(ESCRIPTION_FIELD, '[^0-9.]+', ''), '999999999999.99999', 'NLS_NUMERIC_CHARACTERS = ''.,''') from <your table>;
EDIT
If more decimal point are found in the string and you can accept that some are skipped then this might work.
select to_number(regexp_substr(regexp_replace(ESCRIPTION_FIELD, '[^0-9.]+', ''),'([0-9]*.[0-9]*)'), '999999999999.99999', 'NLS_NUMERIC_CHARACTERS = ''.,''') from <your table>;

Cannot increment properly in primary key when its datatype is varchar

Ii want to generate a primary key like 'A-1','A-2','A-3'...upto 'A-1000'. The datatype of this column is varchar(7) and table name is sesson. I used substring query like max(substring(id,3,4)) as max_id.increament is done in front end code. My query working properly from 'A-1' to 'A-10'. When 'A-10' is save in database then it show '9' as max_id but it should display '10' as max_id. what changes are required to do this?
select max(substring(id,3,4)) as max_id from sesson
When 'A-10' is saved then it should display '10' as max_id but it show '9' as max_id.
use to_number before the max function as shown below.
select max(to_number(substr(id,3,4))) as max_id from sesson
by default system will consider the result of the substring as a string.. hence the max value will be calculated based on ascii value .. character by character. So, 9 will have the highest ascii value than 10(as system will compare based on character by character, 9 will be compared with 1)
Convert the value to a number. In SQL Server, you do:
select max(try_convert(int, substring(id, 3, 4))) as max_id
from sesson

How to Select Only Real Number Data in SQL

How to show only real number with SQL Select ?
As example the data in table appear like :
Price
100000
10.0022
99980
0.0005
1.5
25000
0
20.5
The price data type is number.
I try SQL query like this but fail :
SELECT price FROM price_table WHERE price LIKE '%.%';
The query return error :
ERROR: operator does not exist: numeric ~~ unknown
LIKE is for strings, not for numbers. If you want to find those that do have decimal digits, compare them to their integer value:
select *
from price_table
where price::int <> price;
To be able to use LIKE you need to first convert the number to a string:
select *
from price_table
where price::text like '%.%';

Oracle SQL, separating a Number string to get whole number and decimal separately

I am working with Oracle SQL, and I have a table with an attribute POST (VARCHAR2(10 BYTE)). The data that I am given would be in the format 123.34, and I need one VARCHAR2 (123) and one NUMBER (0.34) to store this in a different table. I was trying to think of a way to do this in a select statement, but could not figure it out.
Can you be assured that the value will always be a NUMBER? If so, then just use TO_NUMBER():
WITH t1 AS (
SELECT '123.34' AS post FROM dual
)
SELECT post_num, TRUNC(post_num), post_num - TRUNC(post_num) FROM (
SELECT TO_NUMBER(post) AS post_num FROM t1
);
If you are not sure that it will always be a NUMBER, there are "safe" ways of converting a character to a value, e.g., using REGEXP_SUBSTR():
TO_NUMBER(REGEXP_SUBSTR(post, '^\d*(\.\d+)?'))
select '123.45' num,
substr('123.45',1,instr('123.45','.')-1) wholenum,
substr('123.45',instr('123.45','.')+1) decimalpart
from dual
Output:
NUM WHOLENUM DECIMALPART
123.45 123 45
Just Floor the Number to get the whole number and reduce the floor value from the number will give the decimal
select 1576.34653 num, floor(1576.34653) whole, 1576.34653-floor(1576.34653) dec
from dual

How to remove currency symbol from price column?

I have a price column which is string and has price for the product from all over the world , now When I try to perform any operation like sum I am getting error.
So my question is how can I remove currency symbol from price column for all the countries?
Here is my sample input:-
locale price
cs_CZ 2462475,38 K
da_DK kr 591.872,50
de_AT 267,70
de_CH CHF 1'998.99
de_DE 1.798,09
en_AE AED7,236.20
en_AU $1,699.00
en_BD Tk999,999.00
en_HK HK$6,188.00
en_HU Ft344,524,655.48
tr_TR 2.344.697,66 TL
Postgres offloads most locale handling to the operating system. So the Postgres currency conversion routines will only work for you if the OS understands the locale names, and your price strings match its expected format.
For example, Windows won't accept da_DK as a locale, and even if it did, it will not accept the string kr 591.872,50, as it expects the Danish currency symbol to be kr. instead of kr.
That said, I think this should work reasonably well on a Linux-based server:
CREATE FUNCTION convert_currency(amount TEXT, locale TEXT) RETURNS NUMERIC AS
$$
BEGIN
PERFORM set_config('lc_monetary', locale || '.UTF-8', True);
RETURN amount::MONEY::NUMERIC;
END
$$
LANGUAGE plpgsql
SET lc_monetary TO DEFAULT;
You seem to have both decimal point and decimal comma but always two decimals (hopefully in the rest of the data too).
You can start by putting those values in a value list for testing (adding extra single quotes where needed).
Then you have to trim out spaces and letters with regular expressions. In the inner SELECT you get the substring with single quotes and commas for thousand separators still in it.
In the outer SELECT you replace the decimal commas in the decimal side and strip out thousand separators on the integer side. The result is cast to type numeric with which you can count sums etc.
SELECT (
regexp_replace(left(substring, length(substring) -3),'[.,'']','','g')
|| replace(right(substring, 3),',','.'))::numeric,
*
FROM (
SELECT substring(column1 from '(([0-9]+[,.''])*[0-9]+[.,][0-9]{2})[^0-9]*$'),
column1
FROM (
VALUES ('2462475,38 K'),
('kr 591.872,50'),
('267,70'),
('CHF 1''998.99'),
('1.798,09'),
('AED7,236.20'),
('$1,699.00'),
('Tk999,999.00'),
('HK$6,188.00'),
('Ft344,524,655.48'),
('2.344.697,66 TL')
) currencies
) sq1;
The following is the whole answer compatible with 9.0 version of PostgreSQL (no left() or right() functions used). Also values list is replaced with a SELECT query that you can replace with your own table and column. Finally it's all been enclosed in a SELECT query that demonstrates the use of the sum-function.
SELECT sum(numeric) FROM (
SELECT (
regexp_replace(substr(substring, 0, length(substring) -3),'[.,'']','','g')
|| replace(substr(substring, length(substring) -3, length(substring)),',','.'))::numeric,
*
FROM (
SELECT substring(column1 from '(([0-9]+[,.''])*[0-9]+[.,][0-9]{2})[^0-9]*$'),
column1
FROM (
SELECT column1 FROM your_table
) currencies
) sq1
) sq2