what is an equivalent of NANVL(oracle function) in postgres - oracle

I am trying to migrate an oracle DB application to postgres.
I can't seem to find an equivalent of NANVL in postgres. Is there a way to emulate the same ?
From the Oracle manual:
The NANVL function is useful only for floating-point numbers of type BINARY_FLOAT or BINARY_DOUBLE. It instructs Oracle Database to return an alternative value n1 if the input value n2 is NaN (not a number). If n2 is not NaN, then Oracle returns n2.

In Postgres you can compare a numeric value to the special constant 'NaN':
select some_column = 'NaN'
from some_table;
So you can write a function that will implement nanvl():
create function nanvl(p_to_test numeric, p_default numeric)
returns numeric
as
$$
select case
when p_to_test = 'NaN' then p_default
else p_to_test
end;
$$
language sql;

Edit
Of course, a_horse_with_no_name is correct.
Everything is simpler: just a CASE with check on equality to NaN is needed http://sqlfiddle.com/#!17/37528
Everything below is incorrect.
Similar to Jorge Campos advice: rip-off isnumeric from isnumeric() with PostgreSQL
and modify it to:
/* Ripoff https://stackoverflow.com/questions/16195986/isnumeric-with-postgresql */
CREATE OR REPLACE FUNCTION nanvl(text, NUMERIC) RETURNS NUMERIC AS $$
DECLARE x NUMERIC;
BEGIN
x = $1::NUMERIC;
RETURN x;
EXCEPTION WHEN others THEN
RETURN $2;
END;
$$
STRICT
LANGUAGE plpgsql IMMUTABLE;
GO
SQLFiddle: http://sqlfiddle.com/#!17/aee9d/1

Related

oracle produce different result for two same queries when only change field,value

I have SQL query . When I declare variable and run the query it produce 39 rows but when I use a literal value instead of variable and run query it produce three rows. How can it be ?
Here is query and results with variables
declare
fromnode number :=1;
CURRENTESTIMATE number :=0;
begin
for ts in (SELECT e.fromnode,e.tonode,e.weight
FROM TS_DIJNODEESTIMATE N
INNER JOIN TS_EDGE E ON N.ID=E.TONODE
WHERE N.DONE=0 AND E.FROMNODE=fromnode AND (CURRENTESTIMATE+E.WEIGHT)<N.ESTIMATE)
loop
dbms_output.put_line(ts.fromnode || ',' || ts.tonode || ',' || ts.weight);
end loop;
end;
The result is
1,2,1306
1,5,2161
1,6,2661
2,3,919
2,4,629
3,2,919
3,4,435
3,5,1225
3,7,1983
4,2,629
4,3,435
5,3,1225
5,6,1483
5,7,1258
6,5,1483
6,7,1532
6,8,661
7,3,1983
7,5,1258
7,6,1532
7,9,2113
7,12,2161
8,6,661
8,9,1145
8,10,1613
9,7,2113
9,8,1145
9,10,725
9,11,383
9,12,1709
10,8,1613
10,9,725
10,11,338
11,9,383
11,10,338
11,12,2145
12,7,2161
12,9,1709
12,11,2145
With literal instead of variable:
declare
fromnode number :=1;
CURRENTESTIMATE number :=0;
begin
for ts in (SELECT e.fromnode,e.tonode,e.weight
FROM TS_DIJNODEESTIMATE N
INNER JOIN TS_EDGE E ON N.ID=E.TONODE
WHERE N.DONE=0 AND E.FROMNODE=1 AND (0+E.WEIGHT)<N.ESTIMATE)
loop
dbms_output.put_line(ts.fromnode || ',' || ts.tonode || ',' || ts.weight);
end loop;
end;
the result is
1,2,1306
1,5,2161
1,6,2661
The desired result is the second result.
You should change the name of your variable fromnode to my_fromnode or something like that.
In your firt query inside where the E.FROMNODE=fromnode is actually comparing the column with itself and returns true.
The problem is naming: you gave your variable the same name as the table column:
E.FROMNODE=fromnode
Oracle's scoping rules resolve names using the nearest match, starting from the innermost point, working outwards and upwards. The nearest namespace in a query is the table so it first attempts to resolve fromnode as a table column. The name fits, so the compiler doesn't look any further.
Effectively your WHERE clause filter is logically the same as 1 = 1, and that's why you get more rows. This should not be a surprise as it is the documented behaviour. The PL/SQL documentation covers scope of variable identifier names here and the interaction with variable names here. The key point is the one Alex Poole highlights:
"If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence."
You thought you had avoided this by putting fromnode in lower case but PL/SQL is not case sensitive. The correct approach is to use a naming convention such as identifying variables with a prefix: V for variable - v_fromnode - or L for Local - l_fromnode - are both common conventions.

Can I write a function that behaves like REGEXP_LIKE, ie the result type of which behaves as a boolean?

In Oracle, usually, SQL functions that should return true/false return 1/0 because the BOOLEAN data type only exists in PL/SQL blocks.
For example with Oracle Text, you must not forget the > 0 in SELECT * FROM bartbl WHERE CONTAINS(foocol, 'sometext') > 0. Otherwise you get ORA-00920 invalid relational operator.
However, it seems that REGEXP_LIKE behaves like a true boolean: you can do SELECT * FROM bartbl WHERE REGEXP_LIKE(foocol, 'sometext') and it works.
So I would like to know why this is the case and if there is a possibility for me to write some functions like this?
Once upon a time, there was no boolean requirement in ANSI SQL (not sure if that requirement exists in latest standards?), and Oracle tends to do their own thing sometimes ;) But, personally I don't see the issue in using Y/N or 1/0.
Anyway, REGEXP_LIKE is really a type of filter (can reduce the number of rows returned), not the same as a typical function (which will return a value (or null) for each row it processes). But good thing is that if you really want to mimic the filter functionality, you can use REGEXP_LIKE with your own function. For example:
create or replace function is_big_number(i_num in number)
return varchar2
as
begin
if (i_num >= 1000000) then
return 'Y';
end if;
return 'N';
end;
with x as (
select 50000 as num from dual
union all
select 1000000 as num from dual
union all
select 3 as num from dual
)
select num, is_big_number(num)
from x
-- this is a filter, not a typical "function"
where
regexp_like(is_big_number(num), 'Y');
Output:
NUM IS_BIG_NUMBER(NUM)
1000000 "Y"
Hope that helps.
Oracle's built in functions are special, you can't write one like this yourself. I don't know why though

Oracle create operator

I've recently run into a case where a fuzzy-match was useful when categorizing historical unstructured-string data. UTL_MATCH is great and has worked well when wrapping into a truthy fuzzy-match function. But I wanted to be joining ad-hoc, and took the route of building out a new function-based operator.
OPERATOR creation: (function-based, FUZZY_MATCH returns 0 for unmatched, 1 for match. A dummy version is included here)
CREATE OR REPLACE FUNCTION FUZZY_MATCH(
LEFT_ITEM VARCHAR2,
RIGHT_ITEM VARCHAR2 )
RETURN NUMBER AS BEGIN
RETURN 1;
END;
CREATE OR REPLACE OPERATOR RESEMBLES
BINDING (VARCHAR2, VARCHAR2)
RETURN NUMBER USING FUZZY_MATCH
;
Creating a new operator works fine, but I've been a little dissatisfied with the resultant sql syntax (example below).
CREATE TABLE LEFT_TABLE(ARBITRARY_DATA VARCHAR2(200) NOT NULL);
CREATE TABLE RIGHT_TABLE(ARBITRARY_DATA VARCHAR2(200) NOT NULL);
INSERT INTO LEFT_TABLE VALUES ('In a hole in the ground there lived a hobbit.');
INSERT INTO RIGHT_TABLE VALUES ('In the ground there lived a hobbit.');
SELECT
LEFT_TABLE.ARBITRARY_DATA LEFT_DATA,
RIGHT_TABLE.ARBITRARY_DATA RIGHT_DATA
FROM
LEFT_TABLE
INNER JOIN
RIGHT_TABLE
ON 1 = RESEMBLES ( LEFT_TABLE.ARBITRARY_DATA, RIGHT_TABLE.ARBITRARY_DATA )
;
Is there an alternative OPERATOR definition to make the truthiness of the operator implicit, allowing for more natural syntax like the following? I'm on 11gR2
Thanks
SELECT
LEFT_TABLE.ARBITRARY_DATA LEFT_DATA,
RIGHT_TABLE.ARBITRARY_DATA RIGHT_DATA
FROM
LEFT_TABLE
INNER JOIN
RIGHT_TABLE
ON LEFT_TABLE.ARBITRARY_DATA RESEMBLES RIGHT_TABLE.ARBITRARY_DATA;

T-SQL isnumeric() replacement in hive

What is the replacement for the T-SQL isnumeric() function in hive 0.10?
http://technet.microsoft.com/en-us/library/ms186272.aspx
There isn't a direct equivalent in HIVE but you can use the cast function.
Casting anything that isn't "numeric" to double will return null and you could use it like this :
select x from table where cast(field as double) is not null
You can check if a number is decimal or not by the below check. This can be given in where clause as well.
select case when abs(x%1)>0 then x else cast(x as bigint) end ;

How to perform a search and replace on a NCLOB?

With an Oracle 11g database, and a NCLOB column in a table, I'm trying to replace some text with another text. At a high level, it's pretty simple, I did it successfully on a SQL Server version of a SQL script, but with Oracle, things are getting complicated, mainly because of data in NCLOB column can be easily more than 46k in length.
With an error ORA-22835 (Buffer too small for CLOB to CHAR or BLOB to RAW conversion), the action, as suggested, is not possible because of variable length of data, and chunking this data with SUBSTR could split in middle of my "search string" to be found in data.
I'm looking for a straightforward and simple solution I could use in a SQL script.
Here is an example of script I'm using with SQL Server:
DECLARE #replacestring NVarChar(MAX) = '0D0D000402175300008950.. very long string 46k+ in length ..1CA68848EEB58360000000049454E44AE426082'
DECLARE #oldFingerprintStart NVarChar(MAX) = '0D0D0004002BA80000FFD8FFE000104A46494600010201004800480000FFE10B304578696600004D4D002A0000000800070';
DECLARE #oldFingerprintEnd NVarChar(MAX) = '02800A002800A002800A002800A002800A002800A002800A002800A002800A002800A002800A002800A002800A002803FFD9';
UPDATE Table1
SET datacolumn =
CONCAT(
SUBSTRING(datacolumn, 0, CHARINDEX(#oldFingerprintStart, datacolumn)),
#replacestring,
SUBSTRING(datacolumn, CHARINDEX(#oldFingerprintEnd, datacolumn) + LEN(#oldFingerprintEnd), LEN(datacolumn) - (CHARINDEX(#oldFingerprintEnd, datacolumn) + LEN(#oldFingerprintEnd))+1)
)
WHERE CHARINDEX(#oldFingerprintStart, datacolumn) > 0
AND CHARINDEX(#oldFingerprintEnd, datacolumn) > 0
You can find nice and detailed explanation here, but as for my experience (and as it is stated in Oracle documentation), standard REPLACE function works on NCLOB fields the same way, as on VARCHAR2.
UPDATE a_table
SET that_field = REPLACE(that_field, 'XYZ', 'ABC')
WHERE CONTAINS(that_field, 'XYZ') > 0
And this way you will avoid any trouble with buffer overflow, as there is none to take care of.

Resources