Search varchar2 for a format? - sql

How do you query varchar2 for a format? Example is Varchar2 field containing date as mm/dd/yyyy '01/01/2015'. How would I find a date that was written as yyyy/mm/dd '2015/01/01' as I do not know the incorrectly formatted date so i can't literally search it. I just want to write where FIELD_1 like '####/##/##' but that obviously does not work.

Of course what you want to do works, if you use regular expressions:
where regexp_like(field_1, '^[0-9]{4}/[0-9]{2}/[0-9]{2}$')
As a side note: you should not be storing dates in the database as strings. You should be using dates instead.

Gordon's answer is probably good enough for what you need, but it may be an issue that the regular expression he gave you will match things like 9999/99/99 that are not dates.
If you really need to match dates -- and if the date is the entire value of the VARCHAR2 and not just embedded somewhere in it -- then a function can help.
In the below example, I create a function called safe_to_date that returns a NULL if the given VARCHAR2 value is not a date in the specified format.
CREATE OR REPLACE FUNCTION safe_to_date ( p_string VARCHAR2 ) RETURN DATE IS
BEGIN
RETURN to_date(p_string,'YYYY/MM/DD');
EXCEPTION
WHEN others THEN
RETURN NULL;
END;
with my_table AS
( SELECT '9999/99/99' /*invalid*/ my_column FROM dual UNION ALL
SELECT '2015/12/25' from dual UNION ALL
SELECT 'not a date at all' /*invalid*/ FROM dual UNION ALL
SELECT '11-NOV-1917' from dual UNION ALL
SELECT '2015*DEC*25' from dual UNION ALL
SELECT '2015/02/28' from dual UNION ALL
SELECT '2015/02/29' /*invalid*/ from dual)
SELECT *
FROM my_table
WHERE safe_to_date(my_column) IS NOT NULL;
Expected results:
25-DEC-2015
11-NOV-1917
25-DEC-2015
28-FEB-2015

Related

Why isn't Oracle converting characters to numbers?

Oracle throws ORA-01722: invalid number in my SQL query and it is unclear why.
I have a table called "LIGHTS" and I want to get the lights with a WATTAGE <= 3. WATTAGE is stored as a VARCHAR2(40) for some reason, but each character does seem to be an integer or float. When I convert WATTAGE to a number using the query:
SELECT TO_NUMBER(WATTAGE) FROM LIGHTS
There's no problem. I get a result like this:
TO_NUMBER(WATTAGE)
1
7
-1
0
15
17.5
However, when I add a WHERE condition to filter the numbers for those less than 3, I get the ORA-01722: invalid number error:
SELECT WATTAGE FROM LIGHTS
WHERE TO_NUMBER(WATTAGE) <= 3
What could be going wrong?
ORA-01722: invalid number comes from the TO_NUMBER(), not from the conditional. I.e., try this and you'll get the same error:
SELECT TO_NUMBER('test') FROM dual;
This would indicate that at least one of your values is not numeric.
Alas Oracle doesn't have a simple way to check whether a string is in fact representing a number. (One of the many reasons to use the correct data type in the first place!)
However, you can write your own. Here is just a brief demo of this concept. I create a table with a column of VARCHAR2 data type, and populate it with a few strings, one of which is not a number.
create table tbl (nbr varchar2(100));
insert into tbl
select '103' from dual union all
select '-1.3' from dual union all
select 'abc' from dual
;
Then I create a small function with a nested block that should error out if TO_NUMBER fails. The error handler will "do something" specific to errors and then return control to the main function. Then I can use this in a WHERE clause. Here are the function and then how it can be used to find the offending values:
create or replace function not_a_number(str varchar2)
return varchar2
as
x number;
r varchar2(100);
begin
begin
x := to_number(str);
exception
when others then
r := str;
end;
return r;
end;
/
select nbr
from tbl
where not_a_number(nbr) is not null;
NBR
-------
abc

how to add two parameters and get value in sql (convert code pl/sql to sql )

function CF_TOTAL_AMTFormula return Number is
begin
RETURN NVL(:AMOUNT,0)+ NVL(:CF_TAX,0);
end;
This function is created in PL/SQL and I want to create this function in SQL.
As OldProgrammer mentioned above, "SQL does not have user-defined functions". Maybe you just want to add two parameters together in SQL? It's pretty straightforward...
select NVL(:AMOUNT,0) + NVL(:CF_TAX,0) as CF_TOTAL_AMT from dual;
Well, SQL does allow inline functions, but they are implemented in PL/SQL so I'm not sure whether they meet your requirement:
with function cf_total_amtformula
( amount number, cf_tax number )
return number
as
begin
return nvl(amount,0) + nvl(cf_tax,0);
end;
select amount, cf_tax
, cf_total_amtformula(amount, cf_tax) as formula_result
from -- inline view to provide demo data:
( select 123 as amount, .2 as cf_tax from dual
union all
select 123, null from dual
union all
select null, .2 from dual )
(requires Oracle 12.1 or later.)
Obviously you could just use nvl(amount,0) + nvl(cf_tax,0) directly without defining any function, or define a column in a view etc.
Putting Bind parameters into a function definition doesn't work, and results in a PLS-00049: bad bind variable error. I think what you are looking for is this:
function CF_TOTAL_AMTFormula(AMOUNT number, CF_TAX number) return Number is
begin
RETURN NVL(AMOUNT,0)+ NVL(CF_TAX,0);
end;
Note that both AMOUNT and CF_TAX have had the leading colons : removed, and have been moved up to the function signature.
Once compiled with create or replace ... you can then call the function in SQL like so:
select CF_TOTAL_AMTFormula(121, 12.1) from dual;
select amount, cf_tax
, nvl(amount,0) + nvl(cf_tax,0)
from -- inline view to provide demo data:
( select 123 as amount, .2 as cf_tax from dual
union all
select 123, null from dual
union all
select null, .2 from dual )

Save value into a variable in oracle db

I have a query where i have to use the result of it multiple times. So instead of running the query multiple times i want to save the query value into a variable once and use it multiple times to accelerate the query speed.
for example:
Declare VAr = select M_DATE from TBT
If you want to do this in an interactive client, the answer depends on the client. For SQLPlus you could do this:
VARIABLE my_date VARCHAR2(10);
BEGIN
SELECT to_char(sysdate, 'YYYY-MM-DD' ) INTO :my_date FROM dual;
END;
/
PRINT my_date;
SELECT * FROM my_table WHERE date_column = TO_DATE( :my_date, 'YYYY-MM-DD' );
Note that SQLPlus does not support a DATE type for its variables, so you need to convert to a string when storing then back to a date when using the value. I recommend always using an explicit conversion and format string, simply to avoid unexpected results if the default format is not what you are expecting.
Within a PL/SQL block or procedure, it would be a little simpler:
DECLARE
l_mydate DATE;
BEGIN
SELECT sysdate INTO l_mydate FROM dual;
SELECT whatever INTO l_whatever FROM my_table
WHERE date_column = l_mydate;
<etc...>
END;
/
If you want to store the result of the query then you need to use a select ... into;
var v_storedate VARCHAR2(19);
exec select m_date into :v_storedate from tbt;
print v_storedate;
For an anonymous SQL block
begin
select m_date
into :v_storedate
from tbt;
end;
/

Is it possible to convert a varchar2 column to a user defined type?

I have a type declared as
TYPE "TABLE_TYPE" AS TABLE OF varchar2(4000)
How can i convert a column i am selecting in a query to return that type.
For example
Select cast(to_char(sysdate) as TABLE_TYPE) from dual
Returns an ORA-00932: inconsistent datatypes: expected - got CHAR error. Is there a way to convert a column which is a varchar2 to be of the user defined type?
If you really want to return a collection with a single element, you would just need to call the type's constructor
SQL> create type table_type as table of varchar2(100);
2 /
Type created.
SQL> select table_type( to_char( sysdate ))
2 from dual;
TABLE_TYPE(TO_CHAR(SYSDATE))
-----------------------------------------------------------------------------
TABLE_TYPE('14-FEB-13')
Normally, though, when you are creating a collection, the intent is to populate it with multiple elements by running a query that returns multiple rows. For that, you would want to do something like
DECLARE
l_strs table_type;
BEGIN
SELECT ename
BULK COLLECT INTO l_strs
FROM emp;
<<l_strs now contains 1 element for each row in the EMP table>>
END;
Use cast + multiset
select cast(multiset(
select to_char(sysdate-level, 'YYYYMMDD')
from dual
connect by level <= 10
) as t_vchar_tab)
from dual;
Union causing an ORA-01790: expression must have same datatype as corresponding expression
CREATE OR REPLACE TYPE varchar2_ntt AS TABLE OF VARCHAR2(4000);
/
SELECT deptno
, CAST(COLLECT(ename) AS varchar2_ntt) AS emps
FROM scott.emp
GROUP BY deptno
/
select table_type(to_char(sysdate)) from dual;

SQL: how do I find if the contents of a varchar column are numeric?

One of the columns in my table is a varchar that is supposed to contain only numeric values (I can't change the definition to a number column). Thus my SQL query:
select to_number(col1) from tbl1 where ...
fails because for some row the contents of the column are not numeric.
What's a select query I can use to find these rows ?
I'm using an Oracle database
I'm looking for something like a is_number function.
There is no native "isnumeric" function in oracle, but this link shows you how to make one:
http://www.oracle.com/technetwork/issue-archive/o44asktom-089519.html
CREATE OR REPLACE FUNCTION isnumeric(p_string in varchar2)
RETURN BOOLEAN
AS
l_number number;
BEGIN
l_number := p_string;
RETURN TRUE;
EXCEPTION
WHEN OTHERS THEN
RETURN FALSE;
END;
/
I don't disagree that the best solution is to follow Tom Kyte's examples others have linked to already. However, if you just need something that is SQL only because you unfortunately do not have the relationship with your DBA to add pl/sql functions to your schema you could possibly leverage regular expressions to meet a basic need. Example:
select '234', REGEXP_SUBSTR('234','^\d*\.{0,1}\d+$') from dual
union all
select 'abc', REGEXP_SUBSTR('abc','^\d*\.{0,1}\d+$') from dual
union all
select '234234abc', REGEXP_SUBSTR('234234abc','^\d*\.{0,1}\d+$') from dual
union all
select '100.4', REGEXP_SUBSTR('100.4','^\d*\.{0,1}\d+$') from dual
union all
select '-100.4', REGEXP_SUBSTR('-100.4','^\d*\.{0,1}\d+$') from dual
union all
select '', REGEXP_SUBSTR('','^\d*\.{0,1}\d+$') from dual
Below is the output of the above:
INPUT RESULT
234 234
abc -
234234abc -
100.4 100.4
-100.4 -
Read this: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1794145000346577404
You may need to decide for yourself what is numeric.
Oracle will happily convert character strings in scientific notation (eg '1e10') to numbers, but it will baulk at something like '1,000' (because of the comma).