Replacing only 3rd to 6th position of a String - oracle

I am new to SQL. I want to replace only 3rd to 6th(only 1st 1234) position of below string with '1219', but its replacing whole new string:
SELECT REPLACE('DD123412341234',SUBSTR('DD123412341234',3,4),'1219' ) FROM DUAL;
Kindle suggest on the same.

You can do it with function regexp_replace.
select regexp_replace('DD123412341234','....','1219',3,1) as RESULT from DUAL
As stated in the documentation, the second function parameter is the regular expression to search for which, in this case, is any four characters. The third parameter is the replacement string. The fourth [optional] parameter indicates to start the search from the third character and the final [also optional] parameter means only search for the first occurrence and replace that first occurrence with 1219.
The result of the above query is DD121912341234

Regular expressions tend to be relatively slow. Since you know the specific location to replace an alternative would be sub the parts to be kept and rebuild with the concatenation operator. The following may be faster, but it does involve more operations.
with test(t) as
(select 'DD123412341234' from dual)
select substr(t,1,2) || '1219' || substr(t,7)
from test;

Related

Looking for Special characters in MonetDB table columns

I am new to MonetDB, I would like to check if there are any special characters in any of the columns in MonetDB.
For example, I have a test Database and the table name is Lmr. I would be to check if any of the columns in table lmr contains special characters?
The query I tried:
SELECT jk
FROM lmr
WHERE jk like '%[^a-Z0-9]%'
I have multiple columns, so is there any way where I can check all the columns with a special character at once?
The LIKE and ILIKE operators use PCRE internally, but do not expose the same expressive power. Basically you can only use % as wildcards.
Luckily, MonetDB already provides wrappers to the PCRE library. For some reason that I am not aware of, they are not made available by default at the SQL layer.
In order to do that, you only need to create SQL function signatures that link to the code that is already available:
CREATE OR REPLACE FUNCTION pcre_match(s string, pattern string) RETURNS boolean EXTERNAL NAME pcre."match";
CREATE OR REPLACE FUNCTION pcre_imatch(s string, pattern string) RETURNS boolean EXTERNAL NAME pcre."imatch";
CREATE OR REPLACE FUNCTION pcre_replace(s string, pattern string, repl string, flags string) RETURNS string EXTERNAL NAME pcre."replace";
CREATE OR REPLACE FUNCTION pcre_replacefirst(s string, pattern string, repl string, flags string) RETURNS string EXTERNAL NAME pcre."replace_first";
After that (to be done only once in a database), you can do:
SELECT jk
FROM lmr
WHERE pcre_imatch(jk,'[^a-z0-9]');
The second parameter is a regular PCRE pattern.
Mind that you had an error in your example. The range a-Z does not exist, because a comes after Z.
In my example I used the i (ignore case) variant of the function, and only used range a-z.
If you want you can also use Unicode categories and rewrite your example to match everything that is not a letter or a number as:
SELECT jk
FROM lmr
WHERE pcre_imatch(jk,'[^\\p{L}\\p{N}]');
Mind that you need to escape each \, which becomes then \\.
About checking multiple columns at once, assuming that you want to return the rows where the condition is satisfied on any of the given columns, you could do this (for 3 columns here):
SELECT col1,col2,col3
FROM lmr
WHERE pcre_imatch(col1 || col2 || col3,'[^\\p{L}\\p{N}]');
where || is string concatenation.
The problem with this is that it first needs to concatenate all columns together. Because MonetDB is a column-store, it will do this for all rows at once. So it will first materialize in memory (and/or disk) all columns for all rows. I'm not sure how much data you have, but that is potentially very big.
The other approach is of course:
SELECT col1,col2,col3
FROM lmr
WHERE pcre_imatch(col1,'[^\\p{L}\\p{N}]')
OR pcre_imatch(col2,'[^\\p{L}\\p{N}]')
OR pcre_imatch(col3,'[^\\p{L}\\p{N}]');
I think I would choose the second approach, as it definitely has a much smaller memory footprint.

Oracle: Why does to_date() accept 2 digit year when I have 4 digits in format string? - and how to enforce 4 digits?

I want to check a date for correctness. (Let's not talk about the fact, that the date is stored in a varchar please ...)
Dates are stored like DDMMYYYY so for instance 24031950 is a correct date. 240319 is not.
So I do this, when the call works, it's a correct date:
select to_date('24031950','DDMMYYYY') from dual;
But unfortunately this also does not return an error:
select to_date('240319','DDMMYYYY') from dual (why?);
But it's interesting, that this one does not work:
select to_date('190324','YYYYMMDD') from dual;
So, how to enforce a check o 4 digit year with the given format mask?
Thanks!
Quoting the docs:
Oracle Database converts strings to dates with some flexibility. [...]
And I believe that flexibility is what you are seeing. You can turn it off with the fx modifier:
FX: Requires exact matching between the character data and the format model
select to_date('240319','fxDDMMYYYY') from dual;
Gives an ORA-01862 error.
Just an additional note to Mat's answer. fx acts like a switch in the string.
For example TO_DATE('2019-11-5','fxYYYY-MM-DD') gives an ORA-01862 error because exact matching applies for the entire string. If you need exact match only for parts of the string, then use for example
TO_DATE('2019-11-5','fxYYYY-MM-fxDD')
In this case YYYY-MM- has to match exactly, whereas DD applies flexible (or lazy) match.
To check whether it is in correct format without exceptions you can also use regex functions.
One possible way would to be check if the string contains 8 digits:
select REGEXP_INSTR ('24031950', '[0-9]{8}') from dual;
1
select REGEXP_INSTR ('240350', '[0-9]{8}') from dual;
0

How to load data into Oracle using SQL Loader with skipping and merging columns?

I am trying to load data into Oracle database using sqlloader,
My data looks like following.
1|2|3|4|5|6|7|8|9|10
I do not want to load first and last column into table,
I want to load 2|3|4|5|6|7|8|9 into one field.
The table I am trying to load into has only one filed named 'field1'.
If anyone has this kind of experience, could you give some advice?
I tried BOUNDFILLER, FILLER and so on, I could not make it.
Help me. :)
Load the entire row from the file into a BOUNDFILLER, then extract the part you need into the column. You have to tell sqlldr that the field is terminated by the carriage return/linefeed (assuming a Windows OS) so it will read the entire line from the file as one field. here the whole line from the file is read into "dummy" as BOUNDFILLER. "dummy" does not match a column name, and it's defined as BOUNDFILLER anyway, so the whole row is "remembered". The next line in the control file starts with a column that DOES match a column name, so sqlldr attempts to execute the expression. It extracts a substring from the saved "dummy" and puts it into the "col_a" column.
The regular expression in a nutshell returns the part of the string after but not including the first pipe, and before but not including the last pipe. Note the double backslashes. In my environment anyway, when using a backslash to take away the special meaning of the pipe (not needed when between the square brackets) it gets stripped when passing from sqlldr to the regex engine so two backslashes are required in the control file
(normally a pipe symbol is a logical OR) so one gets through in the end. If you have trouble here, try one backslash and see what happens. Guess how long THAT took me to figure out!
load data
infile 'x_test.dat'
TRUNCATE
into table x_test
FIELDS TERMINATED BY x'0D0A'
(
dummy BOUNDFILLER,
col_a expression "regexp_substr(:dummy, '[^|]*\\|(.+)\\|.*', 1, 1, NULL, 1)"
)
EDIT: Use this to test the regular expression. For example, if there is an additional pipe at the end:
select regexp_substr('1|2|3|4|5|6|7|8|9|10|', '[^|]*\|(.+)\|.*\|', 1, 1, NULL, 1)
from dual;
2nd edit: For those uncomfortable with regular expressions, this method uses nested SUBSTR and INSTR functions:
SQL> with tbl(str) as (
select '1|2|3|4|5|6|7|8|9|10|' from dual
)
select substr(str, instr(str, '|')+1, (instr(str, '|', -1, 2)-1 - instr(str
, '|')) ) after
from tbl;
AFTER
---------------
2|3|4|5|6|7|8|9
Deciding which is easier to maintain is up to you. Think of the developer after you and comment at any rate! :-)

make a sequence in oracle database

How can I create a sequence, which has two parts one fixed characters part and another variable integer part like "LTR00001" and the next value in the sequence should be "LTR00002"
You cannot. Sequences are just integers.
But you can select a formatted string from the sequence
SELECT 'LTR' || to_char('09999', the_sequence.nextval) FROM DUAL;

Swap fields inside an Oracle record using couple separator * and elements separator |

In an Oracle table, I have record with COUPLES (string,number) so separated:
Abc|3456*Def|7890*Ghi|9430*Jkl|3534
In the previous example, the couples are:
(Abc,3456)
(Def,7890)
(Ghi,9430)
(Jkl,3534)
I would like to modify each record swapping the order of every couple (first the number, then the string):
3456|Abc*7890|Def*9430|Ghi*3534|Jkl
The separator of the two elements of a couple is pipe (|).
The SEPARATOR BETWEEN COUPLES is asterisk (*).
How can I achieve my objective to swap the order of every couple?
Thank you in advance for your kind cooperation!
Try using regular expressions...now you've got two problems:
select
cola,
regexp_replace(cola, '([^*|]*)\|([^*|]*)(\*|$)','\2|\1\3') as swapped_col
from (
select '3456|Abc*7890|Def*9430|Ghi*3534|Jkl' cola from dual
)
Basically the regex is saying search for everything that isn't a | or a * until you find |, then find everything that isn't a | or * until you find a * or then end of the string. Then swap the two bits and terminate it with the character you found as the final separator (either * or EOL). The bits that are swapped are grouped by the round brackets then in the replace string the numbers denote which is placed where... so the contents of the second set of brackets is put first, then a vertical bar, then the first set of brackets, then the third.
By default, REGEXP_REPLACE will replace every occurrence that it finds of the pattern and replace it

Resources