How Select SUBSTRING in ORACLE - sql

I need select a subtring to get the consecutive number from a table field. My table is:
ORDER_NUM ORDER_DATE ORDER_TYPE LOCATION SALE_TYPE
10501702315618 08/01/17 43223 1050 18
105017023186230 21/01/17 43221 1050 230
The field ORDER_NUM is generated as follows
[LOCATION] + [YY] + [CONSECUTIVE_NUMBER] + [SALE_TYPE]
The length of the fields LOCATION and SALE_TYPE Can be different.So, my query is:
SELECT
SUBSTR(ORDER_NUM,LENGTH (ORDER_TYPE) + 3,LENGTH (ORDER_NUM) - LENGTH ( SALE_TYPE ) ),
ORDER_DATE
FROM
CAT_ORDERS
WHERE
LOCATION = '1050'
AND SALE_TYPE = '18'
The result is
SELECT SUBSTR('10501702315618',7,12) from dual
RESULT: 02315618
Where the index position is:
12345678901234
10501702315618
How i can delete the SALE_TYPE characters from the string? There is another function for this ?
Thank's!

Your description refers to the location, so it's odd that your offset is based on order type. The length of the substring needs to exclude the length of the location and the year as well:
SUBSTR(ORDER_NUM, LENGTH (LOCATION) + 3,
LENGTH (ORDER_NUM) - LENGTH(LOCATION) - 2 - LENGTH (SALE_TYPE))
At the moment you're getting 12 characters from your offset, when you only want six, and there are only actually eight available.
Demo with your data:
WITH CAT_ORDERS (ORDER_NUN, ORDER_DATE, ORDER_TYPE, LOCATION, SALE_TYPE) AS (
SELECT 10501702315618, TO_DATE('08/01/17', 'DD/MM/RR'), 43223, 1050, 18 FROM DUAL
UNION ALL SELECT 105017023186230, TO_DATE('21/01/17', 'DD/MM/RR'), 43221, 1050, 230 FROM DUAL
)
SELECT SUBSTR(ORDER_NUM, LENGTH (LOCATION) + 3,
LENGTH (ORDER_NUM) - LENGTH(LOCATION) - 2 - LENGTH (SALE_TYPE))
FROM CAT_ORDERS;
023156
023186
Your order number duplicates data from other columns, which doesn't seem ideal. It would be simpler to only store the 'consecutive number' part instead, and generate the full order number as a virtual column.

This should remove the sale_type characters from the end:
select
substr(substr(order_num, 0, length(order_num)-length(sale_type)), length(location) + 3)
from t;

select regexp_substr(ORDER_NUM,'^' || LOCATION || '..(.*)' || SALE_TYPE || '$',1,1,'',1)
from mytable;
Since we know now that the length of consecutive_number is fixed (namely, 6):
select regexp_substr(ORDER_NUM,'^' || LOCATION || '..(.{6})',1,1,'',1)
from mytable;
or
select regexp_substr(ORDER_NUM,'(.{6})' || SALE_TYPE || '$',1,1,'',1)
from mytable;

Since we know now that the length of consecutive_number is fixed (namely, 6):
select substr(ORDER_NUM,length(LOCATION)+3,6)
from mytable;
or
select substr(ORDER_NUM,-length(SALE_TYPE)-6,6)
from mytable;

Related

Scalar function throws error while using in SQL

My question is:
Write a query to display user name and password. Password should be generated by concatenating first two characters of user name , length of the user name and last three numbers in the phone number and give an alias name as USER_PASSWORD. Sort the results based on the user name in descending order.
select
name,
concat(substring(name, 1, 2), cast(len(name) as varchar), cast(right(phno, 3) as varchar)) as USER_PASSWORD
from
users
order by
name desc;
I get this error:
cast(len(name) as varchar),
ERROR at line 5: ORA-00906: missing left parenthesis
Thanks
You have five issues:
CONCAT only takes two arguments so you either need CONCAT(a, CONCAT(b, c)) or use the || string concatenation operator a || b || c
CAST requires the data type and length CAST(a AS VARCHAR2(10))
SUBSTRING is not an Oracle function, you want SUBSTR;
LEN is not an Oracle function, you want LENGTH;
RIGHT is not an Oracle function, your want SUBSTR with a negative index.
SELECT name,
concat(
substr(name, 1, 2),
concat(
cast(length(name) as varchar2(10)),
cast(SUBSTR(phno, -3) as varchar2(10))
)
) as USER_PASSWORD
from users
order by name desc;
However, you do not need to explicitly use CAST as you can use an implicit conversion between data types:
SELECT name,
substr(name, 1, 2) || length(name) || SUBSTR(phno, -3) as USER_PASSWORD
from users
order by name desc;
Which, for the sample data:
CREATE TABLE users (name, phno) AS
SELECT 'Benny', '0123111' FROM DUAL UNION ALL
SELECT 'Betty', '4567111' FROM DUAL UNION ALL
SELECT 'Beryl', '2222111' FROM DUAL;
Both output:
NAME
USER_PASSWORD
Betty
Be5111
Beryl
Be5111
Benny
Be5111
fiddle
Which leads to the final point, don't generate obvious passwords; generate random or pseudo-random passwords. Then don't store them as plain text; instead store them as a salted-hash.
Concat() is limited to two arguments in Oracle. Use || instead.
with my_data as (
select 'abcdefg' as name, 12345 as phno from dual
)
select
name,
substr(name, 1, 2) ||
length(name) ||
substr(to_char(phno),-3) as user_password
from my_data
| NAME | USER_PASSWORD |
| --------|---------------|
| abcdefg | ab7345 |
fiddle

SQL query to get 6 digits in the output value?

I have a query that gives the output like
select position_id from per_all_people_F
Position_id
FRT567
GFT890
GFT000876
ABC00046
How do i make sure that after first 3 letters, the numbers have to be 6 digit.
Say for example :
FRT567 should be FRT000567.
GFT890 should be GFT000890
or ABC00046 should be ABC000046
How can i tweak my query to accomodate this change?
You can use
SELECT SUBSTR(position_id,1,3)||LPAD(REGEXP_REPLACE(position_id,'\D+'),6,'0')
FROM per_all_people_F
assuming your data format is similar to the presented samples at all.
first piece : ordinary substring extraction for first three letters
second piece : only digits are extracted from the string by using REGEXP_REPLACE(), then zeroes are left padded to the string upto six characters
then concatenate the pieces by double pipe characters
Using only standard string functions (no regular expressions) - split the string after the initial three letters and concatenate the required number of zeros in the middle. This will work correctly even when there are no digits to begin with (the entire input string is just the three letters).
with
t (position_id) as (
select 'FRT567' from dual union all
select 'GFT890' from dual union all
select 'GFT000876' from dual union all
select 'ABC00046' from dual union all
select 'XQY' from dual
)
select position_id,
substr(position_id, 1, 3) || rpad('0', 9 - length(position_id), '0') ||
substr(position_id, 4) as valid_position_id
from t;
POSITION_ID VALID_POSITION_ID
------------ --------------------
FRT567 FRT000567
GFT890 GFT000890
GFT000876 GFT000876
ABC00046 ABC000046
XQY XQY000000
You can use simple string functions and find the first 3 characters using SUBSTR(position_id, 1, 3) and then concatenate || it with the remaining characters left-padded with zeroes to a length of 6 using LPAD(SUBSTR(position_id, 4), 6, '0'). If you can have 3-characters strings then you can use COALESCE to make sure there are always 6 digits:
SELECT position_id,
SUBSTR(position_id, 1, 3) || LPAD(SUBSTR(position_id, 4), 6, '0')
AS expanded_position_id,
-- Optional version for short strings
SUBSTR(position_id, 1, 3)
|| COALESCE(LPAD(SUBSTR(position_id, 4), 6, '0'), '000000')
AS expanded_position_id2
FROM per_all_people_F
Which, for the sample data:
CREATE TABLE per_all_people_F (position_id) as
SELECT 'FRT567' FROM DUAL UNION ALL
SELECT 'GFT890' FROM DUAL UNION ALL
SELECT 'GFT000876' FROM DUAL UNION ALL
SELECT 'ABC00046' FROM DUAL UNION ALL
SELECT 'ABC' FROM DUAL;
Outputs:
POSITION_ID
EXPANDED_POSITION_ID
EXPANDED_POSITION_ID2
FRT567
FRT000567
FRT000567
GFT890
GFT000890
GFT000890
GFT000876
GFT000876
GFT000876
ABC00046
ABC000046
ABC000046
ABC
ABC
ABC000000
db<>fiddle here

Update ID value to format XXXXXXXX-X using oracle SQL

Table name: TEST
Column name: ID [VARCHAR(200)]
The format of ID is ‘XXXXXXXX-X’, where ‘X’ is a number from 0 to 9.
Additional operations in case above format is not satisfied:
if the ID consists of 9 digits and there is a double dash between eighth and ninth digit , the extra dash is removed (e.g. 08452142--6 -> 08452142-6)
if the ID consists of 9 digits and there is/are space(s) between eighth and ninth digit and/or non-digits and/or non-letter symbol(s) then replace them to dash (e.g. 08452142 - . 3 -> 08452142-3)
if the ID consists 9 digits and starts/ends with non-digits and/or non-letter symbol(s) then delete that symbol(s) up to digit (e.g. 08452142-2.. -> 08452142-2)
if the ID contains only 9 digits then put a dash before the last digit (e.g. 123456789 -> 12345678-9)
I have achieved the necessary format by using the below snippet.
UPDATE TEST
SET ID = (SELECT REGEXP_REPLACE(ID,'^\d{8}-\d{1}$','') AS "ID"
from TEST
WHERE PK = 11;
)
What are the possible ways to add transformations as mentioned in points[1-4] above in a single query?
Using REGEXP_REPLACE, I can achieve ID in above format. But in case format is incorrect, and ID needs to be transformed[like removing extra dash, or adding dash in case 9 digits are received] to achieve satisfactory format, how can that be achieved in a single UPDATE query?
In any case, you need to extract 9 digits from your string in the first step. And then
add a hyphen before the last character. For both steps use regexp_replace() function
with test(id) as
(
select '08452142--6' from dual union all
select '08452142 - . 3' from dual union all
select '08452142-2..' from dual union all
select '123456789' from dual union all
select '1234567890' from dual
)
select case when length(regexp_replace(id,'(\D)'))=9 then
regexp_replace(regexp_replace(id,'(\D)'),
'(^[[:digit:]]{8})(.*)([[:digit:]]{1}$)','\1-\3')
end as id
from test;
ID
----------
08452142-6
08452142-3
08452142-2
12345678-9
<null>
Demo
You can use the following I think:
UPDATE TEST
SET ID = REGEXP_REPLACE(ID,'^\D*(\d{8})\D*(\d)\D*$','\1-\2')
WHERE REGEXP_LIKE(ID,'^\D*(\d{8})\D*(\d)\D*$')
This way you ignore all non-digit charcters and search for a 8-digit number and then an 1-digit number. Take these 2 numbers and put a single '-' in between.
This is a little more generous as you might need but should work with all your provided examples.
I think you want the first 8 digits, then a hyphen, then the 9th digit:
select ( substr(regexp_replace(id, '[^0-9]', ''), 1, 8) ||
'-' ||
substr(regexp_replace(id, '[^0-9]', ''), 9, 1)
)
I tried an approach based on the suggestion by #BarbarosÖzhan:
with source as (
select '02426467--6' id from dual union all
select '02426467-6' id from dual union all
select '02597718 -- . 3' id from dual union all
select '02597718 --dF5 . 3' id from dual union all
select '00120792-2..' id from dual union all
select '..00120792-2..' id from dual union all
select '123456789' id from dual union all
select '1234567890' id from dual
)
select
case
when regexp_like(id, '\d{8}-\d{1}')
then id
else
case
when regexp_like(id, '\d{8}-\d{1}')
then id
else
case
when regexp_count(id, '\d') = 9
then
case
when
regexp_like(
regexp_replace(
regexp_replace(
id, '(\d{8}-)(-)(\d{1})', '\1\3'
), '(\d{8})([^A-Za-z1-9])(\d{1})', '\1-\3'
)
, '\d{8}-\d{1}')
then
regexp_replace(
regexp_replace(
id, '(\d{8}-)(-)(\d{1})', '\1\3'
), '(\d{8})([^A-Za-z1-9])(\d{1})', '\1-\3'
)
else id
end
else id
end
end id_tr
from source
However, in cases 3 and 4, I cannot get rid of the space, dot and alphabets. I think something wrong with the logic in case length is more than 9. I end with "id" as it is so the result is the same without any modifications.
Any suggestions to impprove this?

Is there any function in oracle to keep a string length fixed?

I have a scenario like when a column value exceeds the length of 10 characters, I need to take a sub-string for only 10 characters (left most) but if it is shorter than that it should be left padded with zeroes. I tried the following:
with data1 as (select '1234567890123' as dummy1 from dual)
select CASE when (length(dummy1)>10) then substr(dummy1,1,10) else lpad(dummy1,10,'0') end from data1;
But this seems to me quite a longer way to do. Is there any shorter way to achieve this, maybe an Oracle function?
I tried to Google this but could not find any relevant result.
lpad is enough to do the job :
SELECT LPAD( '1234567890123', 10, '0' ) AS formatted
FROM dual;
Just use SUBSTR and LPAD together:
WITH data ( value ) AS (
SELECT '1234567890123' FROM DUAL UNION ALL
SELECT '1' FROM DUAL
)
SELECT LPAD( SUBSTR( value, 1, 10 ), 10, '0' ) AS formatted
FROM data;
Output:
FORMATTED
----------
1234567890
0000000001

substring, after last occurrence of character?

I need help with this problem:
I have a column named phone_number and I wanted to query this column to get the the string right of the last occurrence of '.' for all kinds of numbers in one single sql query.
example #:
515.123.1277
011.44.1345.629268
I need to get 1277 and 629268 respectively.
I have this so far:
select phone_number,
case when length(phone_number) <= 12
then
substr(phone_number,-4)
else
substr (phone_number, -6) end
from employees;
This works for this example, but I want it for all kinds of # formats.
Would be great to get some input.
Thanks
It should be as easy as this regex:
SELECT phone_number, REGEXP_SUBSTR(phone_number, '[^.]*$')
FROM employees;
With the end anchor $ it should get everything that is not a . character after the final .. If the last character is . then it will return NULL.
Search for a pattern including the period, [.] with digits, \d, followed by the end of the string, $.
Associate the digits with a character group by placing the pattern, \d, in parenthesis (see below). This is referenced with the subexpr parameter, 1 (last parameter).
Here is the solution:
SCOTT#dev> list
1 WITH t AS
2 ( SELECT '414.352.3100' p_number FROM dual
3 UNION ALL
4 SELECT '515.123.1277' FROM dual
5 UNION ALL
6 SELECT '011.44.1345.629268' FROM dual
7 )
8* SELECT regexp_substr(t.p_number, '[.](\d+)$', 1, 1, NULL, 1) end_num FROM t
SCOTT#dev> /
END_NUM
========================================================================
3100
1277
629268
You can do something like this in oracle:
select regexp_substr(num,'[^\.]+',1,regexp_count(num,'\.')+1) last_number from
(select '515.123.1277' num from dual union all
select '011.44.1345.629268' from dual );
Previous to 11gR2 you can use regexp_replace instead regexp_count:
select regexp_substr(num,'[^\.]+',1,length(regexp_replace (num , '[^\.]+'))+1) last_number from
(select '515.123.1277' num from dual union all
select '011.44.1345.629268' from dual );