I want to replace 6 of the last 10 digits in a string with XXXXXX. The length of the string can be 16 or 19.
Using below query:
SELECT REPLACE('0000000000000000000',SUBSTR('0000000000000000000',-10,6), 'XXXXXX') FROM DUAL;
--Actual Output --XXXXXXXXXXXXXXXXXX0
--Expected Output--000000000XXXXXX0000
SELECT REPLACE('1234561234561234561',SUBSTR('1234561234561234561',-10,6), 'XXXXXX') FROM DUAL;
--Actual Output --123XXXXXXXXXXXX4561
--Expected Output--123456123XXXXXX4561
SELECT REPLACE('0004421640006525212',SUBSTR('0004421640006525212',-10,6), 'XXXXXX') FROM DUAL;
--Actual Output --000442164XXXXXX5212
--Expected Output--000442164XXXXXX5212
Why do the first two give the wrong result, and how can I fix the query?
If the length of the string was always 19 you could do:
substr('0004421640006525212', 1, 9) || 'XXXXXX' || substr('0004421640006525212', -4)
With two possible lengths you could use a case expression to decide the second argument for the first substr() call, based on the actual string length; or you could allow for any length (of at least 10, anyway) with:
substr('0004421640006525212', 1, length('0004421640006525212') - 10) || 'XXXXXX' || substr('0004421640006525212', -4)
or with a placeholder/column for brevity:
substr(str, 1, length(str) - 10) || 'XXXXXX' || substr(str, -4)
Or maybe simpler, but slower, you could use a regular expression:
regexp_replace('0004421640006525212', '^(.*?)(.{6})(.{4})$', '\1XXXXXX\3')
The regular expression splits the string into three groups; working backwards, (.{4})$ is a group of exactly four characters at the end of the string; then (.{6}) is a group of exactly six characters (the ones you want to replace); then ^(.*} is a group of any/all the remaining characters from the start of the string. The replacement pattern keeps the first and third groups - with \1 and \3 - and puts the fixed Xs between those. The second group - of six characters - is discarded.
SQL Fiddle getting the values, and a couple of shorter ones, form a table to avoid having to repeat them all; which also shows the first version doesn't work properly with varying lengths.
The replace function replaces every occurrence of one string with another. It doesn't know or care how the second argument is generated; it doesn't know you're getting it from a particular position in the same string.
When you do:
REPLACE('0004421640006525212',SUBSTR('0004421640006525212',-10,6), 'XXXXXX')
the SUBSTR() evaluates to '000652', so it's effectively:
REPLACE('0004421640006525212','000652', 'XXXXXX')
and that does what you want, because that substring only appears once in the original string. But with:
REPLACE('1234561234561234561',SUBSTR('1234561234561234561',-10,6), 'XXXXXX')
the SUBSTR() evaluates to '456123', so it's effectively:
REPLACE('1234561234561234561','456123', 'XXXXXX')
and that appears multiple times in the original string:
1234561234561234561
^^^^^^
^^^^^^
and both of those are replaced. With all zeros it's even worse; the SUBSTR() is now '000000', so it matches three times:
0000000000000000000
^^^^^^
^^^^^^
^^^^^^
and all three of those are replaced.
Related
I have the below strings
'HOLA1_HOLA2_HOLA3_HOLA4'
'HOLA1_HOLA2_HOLA3_HOLA4_HOLA5'
How could I get the part of the string up to the second to last occurrence of the '_' character?
Expected result:
'HOLA1_HOLA2'
'HOLA1_HOLA2_HOLA3'
Use simple (fast) string functions and find the substring up to the second-to-last underscore (rather than using (slow) regular expressions):
SELECT SUBSTR(value, 1, INSTR(value, '_', -1, 2) - 1) AS first_part
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT 'HOLA1_HOLA2_HOLA3_HOLA4' FROM DUAL UNION ALL
SELECT 'HOLA1_HOLA2_HOLA3_HOLA4_HOLA5' FROM DUAL;
Outputs:
FIRST_PART
HOLA1_HOLA2
HOLA1_HOLA2_HOLA3
fiddle
Regarding a regular expression with this behaviour:
You can use a lookahead to get the expected Result
.*(?=_)
A lookahead is a zero length assertion (is not included in the match) that asserts that a match has to be followed by the given expression ( _ in this case). By default regex does a greedy match, therefore the lookahead targets the last underscore in the given text.
Try it here: regex101
I have a string like T_44B56T4 that I'd like to make T_B56T4. I can't use positional logic because the string could instead be TE_2BMT that I'd like to make TE_BMT.
What is the most concise Oracle SQL logic to remove the leftmost grouping on consecutive numbers from the string?
EDIT:
regex_replace is unavailable but I have LTRIM,REPLACE,SUBSTR, etc.
would this fit the bill? I am assuming there are alphanumeric characters, then underscore, and then the numbers you want to remove followed by anything.
select regexp_replace(s, '^([[:alnum:]]+)_\d*(.*)$', '\1_\2')
from (
select 'T_44B56T4' s from dual union all
select 'TXM_1JK7B' from dual
)
It uses regular expressions with matched groups.
Alphanumeric characters before underscore are matched and stored in first group, then underscore followed by 0-many digits (it will match as many digits as possible) followed by anything else that is stored in second group.
If we have a match, the string will be replaced by content of the first group followed by underscore and content of the second group.
if there is no match, the string will not be changed.
It seems that you must use standard string functions, as regular expression functions are not available to you. (Comment under Gordon Linoff's answer; it would help if you would add the same at the bottom of your original question, marked clearly as EDIT).
Also, it seems that the input will always have at least one underscore, and any digits that must be removed will always be immediately after the first underscore.
If so, here is one way you could solve it:
select s, substr(s, 1, instr(s, '_')) ||
ltrim(substr(s, instr(s, '_') + 1), '0123456789') as result
from (
select 'T_44B56T4' s from dual union all
select 'TXM_1JK7B' from dual union all
select '34_AB3_1D' from dual
)
S RESULT
--------- ------------------
T_44B56T4 T_B56T4
TXM_1JK7B TXM_JK7B
34_AB3_1D 34_AB3_1D
I added one more test string, to show that only digits immediately following the first underscore are removed; any other digits are left unchanged.
Note that this solution would very likely be faster than regexp solutions, too (assuming that matters; sometimes it does, but often it doesn't).
If I understand correctly, you can use regexp_replace():
select regexp_replace('T_44B56T4', '_[0-9]+', '_')
Here is a db<>fiddle with your two examples.
Note: Your questions says the left most grouping, but the examples all have the number following an underscore, so the underscore seems to be important.
EDIT:
If you really just want the first string of digits replaced without reference to the underscore:
select regexp_replace(code, '[0-9]+', '', 1, 1)
from (select 'T_44B56T4' as code from dual union all select 'TE_2BMT' from dual ) t
I need help with a Oracle Query
I have a query:
scenario 1: select to_char('1737388250',what format???) from dual;
expected output: 173,7388250
scenario 2: select to_char('173738825034',what format??) from dual;
expected output: 173,738825034
scenario 3: select to_char('17373882',what format??) from dual;
expected output: 173,73882
I need a query to satify all above scenarios?
Can some one help please?
It is possible to get the desired result with a customized format model given to to_char; I show one example below. However, any solution along these lines is just a hack (a solution that should work correctly in all cases, but using features of the language in ways they weren't intended to be used).
Here is one example - this will work if your "inputs" are positive integers greater than 999 (that is: at least four digits).
with
sample_data (num) as (
select 1737388250 from dual union all
select 12338 from dual
)
select num, to_char(num, rpad('fm999G', length(num) + 3, '9')) as formatted
from sample_data
;
NUM FORMATTED
---------- ------------
1737388250 173,7388250
12338 123,38
This assumes comma is the "group separator" in nls_numeric_characters; if it isn't, that can be controlled with the third argument to to_char. Note that the format modifier fm is needed so that no space is prepended to the resulting string; and the +3 in the second argument to rpad accounts for the extra characters in the format model (f, m and G).
You can try
select TO_CHAR(1737388250, '999,99999999999') from dual;
Take a look here
Your requirement is different so you can use substr and concatanation as follows:
select substr(your_number,1,3)
|| case when your_number >= 1000 then ',' end
|| substr(1737388250,4)
from dual;
Db<>fiddle
Your "number" is enclosed in single-quotes. This makes it a character string, albeit a string of only numeric characters. But a character string, nonetheless. So it makes no sense to pass a character string to TO_CHAR.
Everyone's suggestions are eliding over this and useing and using an actual number .. notice the lack of single-quotes in their code.
You say you always want a comma after the first three "numbers" (characters), which makes no sense from a numerical/mathematical sense. So just use INSTR and insert the comma:
select substr('123456789',1,3)||','||substr('123456789',4) from dual:
If the source data is actually a number, then pass it to to_char, and wrap that in substr:
select substr(to_char(123456789),1,3)||','||substr(to_char(123456789,4) from dual:
How can I select abcdef.txt from the following string?
abcdef.123.txt
I only know how to select abcdef by doing select substr('abcdef.123.txt',1,6) from dual;
You can using || for concat and substr -3 for right part
select substr('abcdef.123.txt',1,6) || '.' ||substr('abcdef.123.txt',-3) from dual;
or avoiding a concat (like suggested by Luc M)
select substr('abcdef.123.txt',1,7) || substr('abcdef.123.txt',-3) from dual;
A general solution, assuming the input string has exactly two periods . and you want to extract the first and third tokens, separated by one . The length of the "tokens" in the input string can be arbitrary (including zero!) and they can contain any characters other than .
select regexp_replace('abcde.123.xyz', '([^.]*).([^.]*).([^.]*)', '\1.\3') as result
from dual;
RESULT
---------
abcde.xyz
Explanation:
[ ] means match any of the characters between brackets.
^
means do NOT match the characters in the brackets - so...
[^.]
means match any character OTHER THAN .
* means match zero or
more occurrences, as many as possible ("greedy" match)
( ... ) is called a subexpression... see below
'\1.\3 means replace the original string
with the first subexpression, followed by ., followed by the THIRD
subexpression.
Replace the substring of anything surrounded by dots (inclusive) with a single dot. No dependence on lengths of components of the string:
SQL> select regexp_replace('abcdef.123.txt', '\..*\.', '.') fixed
from dual;
FIXED
----------
abcdef.txt
SQL>
I am trying to fetch an id from an oracle table. It's something like TN0001234567890345. What I want is to sort the values according to the right most 10 positions (e.g. 4567890345). I am using Oracle 11g. Is there any function to cut the rightmost 10 places in Oracle SQL?
You can use SUBSTR function as:
select substr('TN0001234567890345',-10) from dual;
Output:
4567890345
codaddict's solution works if your string is known to be at least as long as the length it is to be trimmed to. However, if you could have shorter strings (e.g. trimming to last 10 characters and one of the strings to trim is 'abc') this returns null which is likely not what you want.
Thus, here's the slightly modified version that will take rightmost 10 characters regardless of length as long as they are present:
select substr(colName, -least(length(colName), 10)) from tableName;
Another way of doing it though more tedious. Use the REVERSE and SUBSTR functions as indicated below:
SELECT REVERSE(SUBSTR(REVERSE('TN0001234567890345'), 1, 10)) FROM DUAL;
The first REVERSE function will return the string 5430987654321000NT.
The SUBSTR function will read our new string 5430987654321000NT from the first character to the tenth character which will return 5430987654.
The last REVERSE function will return our original string minus the first 8 characters i.e. 4567890345
SQL> SELECT SUBSTR('00000000123456789', -10) FROM DUAL;
Result: 0123456789
Yeah this is an old post, but it popped up in the list due to someone editing it for some reason and I was appalled that a regular expression solution was not included! So here's a solution using regex_substr in the order by clause just for an exercise in futility. The regex looks at the last 10 characters in the string:
with tbl(str) as (
select 'TN0001239567890345' from dual union
select 'TN0001234567890345' from dual
)
select str
from tbl
order by to_number(regexp_substr(str, '.{10}$'));
An assumption is made that the ID part of the string is at least 10 digits.