I am trying to add a character onto the end of a string using
CASE
WHEN PackageNum = 2 THEN to_char(DespatchNum + 'B')
WHEN PackageNum = 3 THEN to_char(DespatchNum + 'C')
WHEN PackageNum = 4 THEN to_char(DespatchNum + 'D')
WHEN PackageNum = 5 THEN to_char(DespatchNum + 'E')
ELSE to_char(DespatchNum)
END as ShipmentReference,
However I am getting the error
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
DespatchNum is a number
As an example, output would look like 1234B
Help appreciated.
You can use:
SELECT packagenum,
despatchnum,
CASE PackageNum
WHEN 2 THEN DespatchNum || 'B'
WHEN 3 THEN DespatchNum || 'C'
WHEN 4 THEN DespatchNum || 'D'
WHEN 5 THEN DespatchNum || 'E'
ELSE TO_CHAR(DespatchNum)
END as ShipmentReference
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name ( packagenum, despatchnum ) AS
SELECT LEVEL, 1234 FROM DUAL CONNECT BY LEVEL <= 6;
Outputs:
PACKAGENUM
DESPATCHNUM
SHIPMENTREFERENCE
1
1234
1234
2
1234
1234B
3
1234
1234C
4
1234
1234D
5
1234
1234E
6
1234
1234
db<>fiddle here
As mentioned by #gsalem in the comments, the concatenation was incorrect and to concatonate characters onto a string you have use || not +
You can also factor out TO_CHAR(DESPATCHNUM), since it's common to all cases.
Something like this:
with
tbl (packagenum, despatchnum) as (
select 2, 3002 from dual union all
select 5, 4122 from dual union all
select 7, 8320 from dual
)
select packagenum, despatchnum,
to_char(despatchnum) ||
case packagenum when 2 then 'B'
when 3 then 'C'
when 4 then 'D'
when 5 then 'E' end as shipmentreference
from tbl
;
PACKAGENUM DESPATCHNUM SHIPMENTREFERENCE
---------- ----------- -----------------
2 3002 3002B
5 4122 4122E
7 8320 8320
Related
Is it possible to count and also group by comma delimited values in the oracle database table? This is a table data example:
id | user | title |
1 | foo | a,b,c |
2 | bar | a,d |
3 | tee | b |
The expected result would be:
title | count
a | 2
b | 2
c | 1
d | 1
I wanted to use concat like this:
SELECT a.title FROM Account a WHERE concat(',', a.title, ',') LIKE 'a' OR concat(',', a.title, ',') LIKE 'b' ... GROUP BY a.title?
But I'm getting invalid number of arguments on concat. The title values are predefined, therefore I don't mind if I have to list all of them in the query. Any help is greatly appreciated.
This uses simple string functions and a recursive sub-query factoring and may be faster than using regular expressions and correlated joins:
Oracle Setup:
CREATE TABLE account ( id, "user", title ) AS
SELECT 1, 'foo', 'a,b,c' FROM DUAL UNION ALL
SELECT 2, 'bar', 'a,d' FROM DUAL UNION ALL
SELECT 3, 'tee', 'b' FROM DUAL;
Query:
WITH positions ( title, start_pos, end_pos ) AS (
SELECT title,
1,
INSTR( title, ',', 1 )
FROM account
UNION ALL
SELECT title,
end_pos + 1,
INSTR( title, ',', end_pos + 1 )
FROM positions
WHERE end_pos > 0
),
items ( item ) AS (
SELECT CASE end_pos
WHEN 0
THEN SUBSTR( title, start_pos )
ELSE SUBSTR( title, start_pos, end_pos - start_pos )
END
FROM positions
)
SELECT item,
COUNT(*)
FROM items
GROUP BY item
ORDER BY item;
Output:
ITEM | COUNT(*)
:--- | -------:
a | 2
b | 2
c | 1
d | 1
db<>fiddle here
Split titles to rows and count them.
SQL> with test (id, title) as
2 (select 1, 'a,b,c' from dual union all
3 select 2, 'a,d' from dual union all
4 select 3, 'b' from dual
5 ),
6 temp as
7 (select regexp_substr(title, '[^,]', 1, column_value) val
8 from test cross join table(cast(multiset(select level from dual
9 connect by level <= regexp_count(title, ',') + 1
10 ) as sys.odcinumberlist))
11 )
12 select val as title,
13 count(*)
14 From temp
15 group by val
16 order by val;
TITLE COUNT(*)
-------------------- ----------
a 2
b 2
c 1
d 1
SQL>
If titles aren't that simple, then modify REGEXP_SUBSTR (add + sign) in line #7, e.g.
SQL> with test (id, title) as
2 (select 1, 'Robin Hood,Avatar,Star Wars Episode III' from dual union all
3 select 2, 'Mickey Mouse,Avatar' from dual union all
4 select 3, 'The Godfather' from dual
5 ),
6 temp as
7 (select regexp_substr(title, '[^,]+', 1, column_value) val
8 from test cross join table(cast(multiset(select level from dual
9 connect by level <= regexp_count(title, ',') + 1
10 ) as sys.odcinumberlist))
11 )
12 select val as title,
13 count(*)
14 From temp
15 group by val
16 order by val;
TITLE COUNT(*)
------------------------------ ----------
Avatar 2
Mickey Mouse 1
Robin Hood 1
Star Wars Episode III 1
The Godfather 1
SQL>
Here I have a source table
Source Table :
ID | Log
____________________
1 | Status : New
| Assignment : 1
| Priority : Low
_____________________
2 | Status : In Progress
Target Table :
ID | Key | Value
____________________
1 | Status | New
1 | Assignment| 1
1 | Priority | Low
2 | Status | In Progress
Please suggest the approach.
Thanks in advance.
Something like this should work. You seem to have some spaces around the actual tokens that must be removed, so I use the TRIM() function for that.
The WITH clause is there just for testing (not part of the SQL solution to your question - remove it before testing it against your actual table and columns).
with
source_table ( id, log ) as (
select 1, 'Status : New
Assignment : 1
Priority : Low' from dual union all
select 2, 'Status : In Progress' from dual
)
select id,
trim(regexp_substr(log, '(' || chr(10) || '|^)([^:]*):', 1, level, null, 2)) key,
trim(regexp_substr(log, ':([^:]*)(' || chr(10) || '|$)', 1, level, null, 1)) value
from source_table
connect by level <= regexp_count(log, ':')
and prior id = id
and prior sys_guid() is not null
;
ID KEY VALUE
-- ------------ --------------
1 Status New
1 Assignment 1
1 Priority Low
2 Status In Progress
Here's yet another option; TEST represents your sample data. INTER splits rows (separated by CHR(10)), while the final SELECT utilizes trivial SUBSTR + INSTR combination.
SQL> WITH test (id, LOG)
2 AS (SELECT 1,
3 'status: new'
4 || CHR (10)
5 || 'assignment: 1'
6 || CHR (10)
7 || 'priority: low'
8 FROM DUAL
9 UNION
10 SELECT 2, 'status: in progress' FROM DUAL),
11 inter
12 AS (SELECT id,
13 REGEXP_SUBSTR (REPLACE (LOG, CHR (10), ';'),
14 '[^;]+',
15 1,
16 COLUMN_VALUE)
17 LOG
18 FROM test,
19 TABLE (
20 CAST (
21 MULTISET (
22 SELECT LEVEL
23 FROM DUAL
24 CONNECT BY LEVEL <= REGEXP_COUNT (LOG, ':') + 1) AS SYS.odcinumberlist)))
25 SELECT id,
26 TRIM (SUBSTR (LOG, 1, INSTR (LOG, ':') - 1)) key,
27 TRIM (SUBSTR (LOG, INSTR (LOG, ':') + 1, LENGTH (LOG))) VALUE
28 FROM inter
29 WHERE LOG IS NOT NULL
30 ORDER BY id;
ID KEY VALUE
---- --------------- ---------------
1 status new
1 assignment 1
1 priority low
2 status in progress
SQL>
If input is comma separated then store the first part in LAST_NAME and second part in FIRST_NAME.
If comma is not present then store the name in LAST_NAME
Can you please help me in achieving the #2 in oracle?
In my approach am unable to store the full_name in last_name column if there is no comma.
Input :
1. FULL_NAME = "MIKE,MYERS"
2. FULL_NAME = "KFC"
Output :
SELECT SUBSTR('FULL_NAME', 0, INSTR('1,2', ',') - 1) LAST_NAME,
SUBSTR('FULL_NAME', INSTR('1,2', ',', -1) + 1) FIRST_NAME FROM DUAL
**LAST_NAME** **FIRST_NAME**
MIKE MYERS
**LAST_NAME** **FIRST_NAME**
KFC
Since you need two separate columns, I think you would need two CASE statements.
For example,
SQL> WITH DATA AS
2 ( SELECT 'MIKE,MYERS' str FROM dual
3 UNION ALL
4 SELECT 'KFC' str FROM dual
5 )
6 SELECT
7 CASE
8 WHEN instr(str, ',') <> 0
9 THEN SUBSTR(str, 1, INSTR(str, ',', 1) - 1)
10 ELSE str
11 END LAST_NAME,
12 CASE
13 WHEN instr(str, ',') <> 0
14 THEN SUBSTR(str, INSTR(str, ',', 1) +1)
15 ELSE NULL
16 END FIRST_NAME
17 FROM DATA
18 /
LAST_NAME FIRST_NAME
---------- ----------
MIKE MYERS
KFC
SQL>
I have log table:
with t as
(select '16/04/2014 20:17:25 XXX. Xxxxxxx xxx xxx [SYSTEM_JOBS] xxx [POSTPONE_JOBS] xxx [SYSTEM] xxxx [JOB2]' col
from dual
UNION ALL
select '16/04/2014 20:17:25 XXX. Xxxxxxx [SYSTEM_JOBS] xxx [POSTPONE_JOBS]' col
from dual)
select * from t
I am trying to extract the CODE between '[' and ']' including [].
My version:
select regexp_substr(col, '(\[.*?\])', 1, level) col_substr,
regexp_replace(regexp_substr(col, '(\[.*?\])', 1, level),
'(\[|])',
'') col_replace_substr
from t
CONNECT BY regexp_substr(col, '(\[.*?\])', 1, level) is not null
My version work good, but i need get result without function regexp_replace, i wanna use one function in my code.
Can I get result only with REGEXP_SUBSTR?
This question popped up in the "Related" section and even though its an old question it is still relevant and deserves to be answered. I'm afraid the original poster is incorrect as his code does not work good but returns invalid results. His example returns 14 rows, where there are only 6 values surrounded by square brackets. Here is an example with a regex that allows for when the matched pattern is followed by the end of the line and also shows the row number and match number for proof. I hope this helps a future searcher:
SQL> with t(rownbr, col1) as (
select 1, '16/04/2014 20:17:25 XXX. Xxxxxxx xxx xxx [SYSTEM_JOBS] xxx [POSTPONE_JOBS] xxx [SYSTEM] xxxx [JOB2]' from dual
UNION
select 2, '16/04/2014 20:17:25 XXX. Xxxxxxx [SYSTEM_JOBS] xxx [POSTPONE_JOBS]' from dual
)
SELECT rownbr, column_value match_number,
regexp_substr(col1,'(\[.*?\])( |$)', 1, column_value, NULL, 1) col_substr,
regexp_substr(col1,'\[(.*?)\]( |$)', 1, column_value, NULL, 1) col_replace_substr
FROM t,
TABLE(
CAST(
MULTISET(SELECT LEVEL
FROM dual
CONNECT BY LEVEL <= REGEXP_COUNT(col1, '\[.*?\]')
) AS sys.OdciNumberList
)
)
WHERE regexp_substr(col1,'(\[.*?\])( |$)', 1, column_value, NULL, 1) IS NOT NULL;
ROWNBR MATCH_NUMBER COL_SUBSTR COL_REPLACE_SUBSTR
---------- ------------ -------------------- --------------------
1 1 [SYSTEM_JOBS] SYSTEM_JOBS
1 2 [POSTPONE_JOBS] POSTPONE_JOBS
1 3 [SYSTEM] SYSTEM
1 4 [JOB2] JOB2
2 1 [SYSTEM_JOBS] SYSTEM_JOBS
2 2 [POSTPONE_JOBS] POSTPONE_JOBS
6 rows selected.
SQL>
How to return multiple records from "CASE DEFAULT".
eg:
Master.COLUMN1 IN (CASE '#InputString'
WHEN 'One' THEN 1
WHEN 'Two' THEN 2
WHEN 'THREE' THEN 3
ELSE (SELECT NUM_BER FROM TABLE1 WHERE COLUMN2 LIKE '%#InputString%')
END)
I tried with passing One and it returns 1. But when I pass 'four' it showed error like ORA-01427 single-row sub query returns more than one row. How can i solve this??
you can try it like this:
column1 in (CASE '#InputString'
WHEN 'One' THEN 1
WHEN 'Two' THEN 2
WHEN 'THREE' THEN 3
END)
OR (column1 in (SELECT NUM_BER FROM TABLE1 WHERE COLUMN2 LIKE '%#InputString%')
and '#InputString' not in ('One', 'Two', 'THREE'));
Here is a sqlfiddle demo
You're getting the ORA-01427 error, because in select query in your else parts returns more than one row.
The below queries will help you:
WITH t AS
(SELECT 1 string FROM dual
UNION
SELECT 2 string FROM dual
UNION
SELECT 1234 string FROM dual
)
SELECT * FROM t
where t.string in ( case 'one' when 'one' then 1 else (select 1234 from dual) end);
output
_____
1
WITH t AS
(SELECT 1 string FROM dual
UNION
SELECT 2 string FROM dual
UNION
SELECT 1234 string FROM dual
)
SELECT * FROM t
where t.string in ( case 'two' when 'one' then 1 else (select 1234 from dual) end);
output
------
1234
You can use decode
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 'A' as string from dual union all
3 select 'B' from dual union all
4 select 'C' from dual union all
5 select 'D' from dual
6 )
7 select string , decode(string, 'A',1,'B',2,3) as string_out
8* from x
SQL> /
S STRING_OUT
- ----------
A 1
B 2
C 3
D 3