oracle SUBSTR to postgres SUBSTR function - sql

I have a function in oracle that i need converted to postgres.
i can't seem to find a reason for this difference in docs docs but:
oracle:
SELECT substr('1236',-4, 4) FROM DUAL;
Result: 1236
postgres:
SELECT substr('1236',-4, 4);
Result: empty (Null)
i need an output similiar to oracle and i cant seem to understand why the postgres function differs, and what i can use as an alternative

I'm a little confused by your confusion. Oracle is quite clear that a negative position counts back from the end of the string.
Nothing in the Postgres documentation suggests that this behavior. There is no mention of negative positions (as far as I can tell) for any string functions other than left() and right(). And no hint whatsoever that negative positions have a special meaning in other contexts.
Postgres fortunately has a simpler way to do what you want:
select right('1236', 4)

Related

Alternative for regexp_replace for BIGINT

I'm quite new at programming with Oracle and DB2 and have a question. I need to mask a field that has a BIGING as datatype. But when i tried to execute a query with regexp_replace, i have this error line SQLCODE=-420, SQLSTATE=22018.
Is there a alternative for a regexp_replace for BIGING.
Thanks a lot!
You can 'mask' integers by replace all digits except first and last by zeroes using next code (Oracle):
select
N, -- source number
FLOOR(N/POWER(10, FLOOR(LOG(10, N)))) * POWER(10, FLOOR(LOG(10, N))) + MOD(N, 10) MASKED
from a;
run sql online
Depending on the platform and version of Db2, you might consider using CREATE MASK if available. That would ensure the data is always masked without needing to do it in every application.
A quick search seems to indicate the Oracle also has similar support but they call it redaction. Masking in oracle seems to be tied into subsetting and exporting data from production to DEV/TEST.
Do you really need a solution for both RDBMs?
And if you really want to roll your own, you need to provide some examples of the masked value you want returned.
EDIT
Here is a part of the code. PK_PERSON has BIGINT as datatype. update
Person.T_PERSON set PK_PERSON = REGEXP_REPLACE(PK_PERSON, '[0-9]',
'*') where PK_PERSON in ('117888')
That's not going to work, you can't set a BIGINT column to a string. That's also not how masking works. Masking generally refers to a process that happens when the data is read out of the DB.

What is the purpose of using `timestamp(nullif('',''))`

Folks
I am in the process of moving a decade old back-end from DB2 9.5 to Oracle 19c.
I frequently see in SQL queries and veiw definitions bizarre timestamp(nullif('','')) constructs used instead of a plain null.
What is the point of doing so? Why would anyone in their same mind would want to do so?
Disclaimer: my SQL skills are fairly mediocre. I might well miss something obvious.
It appears to create a NULL value with a TIMESTAMP data type.
The TIMESTAMP DB2 documentation states:
TIMESTAMP scalar function
The TIMESTAMP function returns a timestamp from a value or a pair of values.
TIMESTAMP(expression1, [expression2])
expression1 and expression2
The rules for the arguments depend on whether expression2 is specified and the data type of expression2.
If only one argument is specified it must be an expression that returns a value of one of the following built-in data types: a DATE, a TIMESTAMP, or a character string that is not a CLOB.
If you try to pass an untyped NULL to the TIMESTAMP function:
TIMESTAMP(NULL)
Then you get the error:
The invocation of routine "TIMESTAMP" is ambiguous. The argument in position "1" does not have a best fit.
To invoke the function, you need to pass one of the required DATE, TIMESTAMP or a non-CLOB string to the function which means that you need to coerce the NULL to have one of those types.
This could be:
TIMESTAMP(CAST(NULL AS VARCHAR(14)))
TIMESTAMP(NULLIF('',''))
Using NULLIF is more confusing but, if I have to try to make an excuse for using it, is slightly less to type than casting a NULL to a string.
The equivalent in Oracle would be:
CAST(NULL AS TIMESTAMP)
This also works in DB2 (and is even less to type).
It is not clear why - in any SQL dialect, no matter how old - one would use an argument like nullif('',''). Regardless of the result, that is a constant that can be calculated once and for all, and given as argument to timestamp(). Very likely, it should be null in any dialect and any version. So that should be the same as timestamp(null). The code you found suggests that whoever wrote it didn't know what they were doing.
One might need to write something like that - rather than a plain null - to get null of a specific data type. Even though "theoretical" SQL says null does not have a data type, you may need something like that, for example in a view, to define the data type of the column defined by an expression like that.
In Oracle you can use the cast() function, as MT0 demonstrated already - that is by far the most common and most elegant equivalent.
If you want something much closer in spirit to what you saw in that old code, to_timestamp(null) will have the same effect. No reason to write something more complicated for null given as argument, though - along the lines of that nullif() call.

extracting special character from a string in oracle sql

I need a query to remove all alphanumeric characters from a string and give me only special characters.
If string is '##45gr##3' query should give me '####'.
SELECT REGEXP_REPLACE('##45gr##3','[^[:punct:]'' '']', NULL) FROM dual;
The old-fashioned way, with a replace() call:
select translate(upper(txt)
, '.1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'
, '.') as spec_txt
from t42
/
With a replace() solution is better to type all the characters we want to exclude. Yes that is the larger set (probably) but at least it's well-defined. We don't want to keep revising the code to handle new characters in the input string.
Obviously regex solution is more compact and who can deny the elegance of #venkatesh solution? However, evaluating regular expressions is more expensive than more focused SQL functions, so it's worth knowing alternative ways of doing things, for those cases when performance is an issue.
Everything written in comments is most probably very true (especially the 5th one that talks about exceptions, special cases etc.). My feeling is that Jörg knows more about regular expressions than I'll ever know.
Anyway, to cut a long story short, in its very simple appearance, regarding the question posted ("remove all numbers and letters"), something like this might work:
SQL> select regexp_replace('a##45gr##3$.', '[[:digit:]]+|[[:alpha:]]+', '') result
2 from dual;
RESULT
------
####$.
SQL>

How do i extract a Date out of a string in SQL?

I can't seems to find out how to extract the date from a string in a column. e.g:
2015-03-30T04:04:26+0200 -> 30.3.2015
I tried to look for a regex query but it didn't work for me.
The following will work in a lot of databases:
select cast(left(col, 10) as date)
Option 1: Pure SQL Solution
i.e. MySQL
since you are asking SQL here, how about:
SELECT DATE_FORMAT(DATE("2015-03-30T04:04:26+0200"),"%d.%m.%Y")
which returns
30.03.2015
i.e. Oracle
If you want to run this query on Oracle, then you will have to adapt the query:
SELECT TO_CHAR(TO_DATE(SUBSTR('2015-03-30T04:04:26+0200', 1, 10),'YYYY-MM-DD'),'DD.MM.YYYY') myDate FROM dual
i.e. PostgreSQL
Also here a few differences in the query. Actually quite the same as Oracle. I cheated here and moved the 0200 at the end into US which is actually for microseconds, but gives us a valid parse format string without having to use SUBSTR:
SELECT TO_CHAR(TO_DATE('2015-03-30T04:04:26+0200','YYYY-MM-DD"T"HH24:MI:SS+US'),'DD.MM.YYYY')
i.e. SQLite
Now here a completely different function. SQLite uses strftime() to format dates. But here i use substr() again:
SELECT STRFTIME('%d.%m.%Y',DATE(SUBSTR('2015-03-30T04:04:26+0200',1,10)))
Now i could go on with MS SQL Server and more but i reckon with these alternatives you should find a way of translating the problem to your DBMS SQL syntax.
Option 2: Pure RegEx Solution
PCRE RegEx (Python, PHP, etc.)
or if you want to use plain regex:
(\d+)-(\d+)-(\d+)T.*
which you substitute with
\3.\2.\1
This you can play around with here:
RegEx Fiddle Demonstration
Javascript, Java, etc.
Depending on your programming language, you might need to change your RegEx to:
([0-9]+)-([0-9]+)-([0-9]+)T.*
and your replacement string to
$3.$2.$1f

Using substr to trim string on Oracle

I want to trim a string to a specified length. If the string is shorter, I don't want to do anything. I found a function substr() which does the job. However there is nothing in the Oracle documentation what happens if the string is shorter, than maximal length.
For example this:
select substr('abc',1,5) from dual;
returns 'abc', which is what I need.
I'd like to ask if this is safe, because the function seems not to be defined for this usage. Is there a better way how to truncate?
It is totally ok, but if you want, you can use this query:
select substr('abc',1,least(5,length('abc'))) from dual;
This is an interesting question. Surprisingly, the documentation doesn't seem to cover this point explicitly.
I think what you are doing is quite safe. substr() is not going to "add" characters to the end of the string when the string is too short. I have depended on this behavior in many databases, including Oracle, over time. This is how similar functions work in other databases and most languages.
The one sort-of-exception would be when the original data type is a char() rather than varchar2() type. In this case, the function would return a string of the same type, so it might be padded with spaces. That, though, is a property of the type not really of the function.
If you want to be absolutely certain that you won't end up with trailing blanks by using SUBSTR alone (you won't, but sometimes it's comforting be really sure) you can use:
SELECT RTRIM(SUBSTR('abc',1,5)) FROM DUAL;
Share and enjoy.
It is better to use the below query
SELECT SUBSTR('abc',1,LEAST(5,LENGTH('abc'))) FROM DUAL;
Above query would either take the length of the string or the number 5 whichever is lower.