I have a given database-table where I am not able to change the database-design.
This table contains a column whare names are saved.
The Column "UserName" contains values like "Clinton Bill" or "Trump Donald" or "Bush George".
I need a select statement which is able to find the primary key for the "Bill Clinton" or "Donald Trump"-row.
So the order of Firstname and Lastname is reversed.
Does anybody know how this can be done.
How the Where-Clouse should look like.
If you want to "reverse" such a string (that contains of two words), you can use substr + instr or regular expressions, e.g.
SQL> with test (username) as
2 (select 'Clinton Bill' from dual)
3 select substr(username, instr(username, ' ') + 1) ||' '||
4 substr(username, 1, instr(username, ' ') - 1) as reversed_value,
5 --
6 regexp_substr(username, '\w+', 1, 2) ||' '||
7 regexp_substr(username, '\w+', 1, 1) as reversed_value_2
8 from test;
REVERSED_VALUE REVERSED_VALUE_2
-------------------- --------------------
Bill Clinton Bill Clinton
SQL>
Now, just re-use it (whichever option you want) in another query, e.g.
select id
from test
where regexp_substr(username, '\w+', 1, 2) ||' '||
regexp_substr(username, '\w+', 1, 1) = 'Bill Clinton'
We have 2 columns in one table in oracle SQL as
Col1= "there is book on the table"
Col2= "there are flowers on the chair"
Now I need the result as differed data in the column3 as new column col3.
The col3 result should be
"are flowers chair".
How to achieve this in oracle SQL??
You can use:
WITH words ( rid, col, name, id, word ) AS (
SELECT rid,
CASE INSTR(col, ' ')
WHEN 0
THEN NULL
ELSE SUBSTR(col, INSTR(col, ' ') + 1)
END,
name,
1,
CASE INSTR(col, ' ')
WHEN 0
THEN col
ELSE SUBSTR(col, 1, INSTR(col, ' ') - 1)
END
FROM ( SELECT ROWID AS rid, col1, col2 FROM table_name )
UNPIVOT ( col FOR name IN (col1, col2) )
UNION ALL
SELECT rid,
CASE INSTR(col, ' ')
WHEN 0
THEN NULL
ELSE SUBSTR(col, INSTR(col, ' ') + 1)
END,
name,
id + 1,
CASE INSTR(col, ' ')
WHEN 0
THEN col
ELSE SUBSTR(col, 1, INSTR(col, ' ') - 1)
END
FROM words
WHERE col IS NOT NULL
),
paired_words ( rid, id1, id2 ) AS (
SELECT c1.rid,
c1.id AS id1,
c2.id AS id2
FROM ( SELECT rid, id, word FROM words WHERE name = 'COL1' ) c1
INNER JOIN
( SELECT rid, id, word FROM words WHERE name = 'COL2' ) c2
ON (c1.rid = c2.rid AND c1.word = c2.word)
),
max_path ( rid, path ) AS (
SELECT rid,
path
FROM (
SELECT rid,
SYS_CONNECT_BY_PATH(id2, ',') || ',' AS path,
ROW_NUMBER() OVER (PARTITION BY rid ORDER BY LEVEL DESC) AS rn
FROM paired_words
CONNECT BY PRIOR rid = rid
AND PRIOR id1 < id1
AND PRIOR id2 < id2
)
WHERE rn = 1
)
SELECT LISTAGG(word, ' ') WITHIN GROUP (ORDER BY id) AS missing
FROM words w
WHERE NOT EXISTS (
SELECT 1
FROM max_path mp
WHERE w.rid = mp.rid
AND mp.path LIKE '%,' || w.id || ',%'
)
AND w.name = 'COL2'
GROUP BY rid;
Which, for the sample data:
CREATE TABLE table_name ( col1, col2 ) AS
SELECT 'there is book on the table', 'there are flowers on the chair' FROM DUAL UNION ALL
SELECT 'there is book on the table', 'there is a book on the table' FROM DUAL UNION ALL
SELECT 'there is book on the table', 'there is book there is book on the table on the table' FROM DUAL
Outputs:
MISSING
are flowers chair
a
there is book on the table
db<>fiddle here
Here's one option (which follows what you asked). Read comments within code.
SQL> with test (id, col1, col2) as
2 (select 1, 'there is book on the table',
3 'there are flowers on the chair'
4 from dual
5 ),
6 -- split sentences into words (each in its own line)
7 sent1 as
8 (select id,
9 column_value cv,
10 regexp_substr(col1, '[^ ]+', 1, column_value) word
11 from test cross join
12 table(cast(multiset(select level from dual
13 connect by level <= regexp_count(col1, ' ') + 1
14 ) as sys.odcinumberlist))
15 ),
16 sent2 as
17 (select id,
18 column_value cv,
19 regexp_substr(col2, '[^ ]+', 1, column_value) word
20 from test cross join
21 table(cast(multiset(select level from dual
22 connect by level <= regexp_count(col2, ' ') + 1
23 ) as sys.odcinumberlist))
24 )
25 -- final result
26 select a.id,
27 listagg(b.word, ' ') within group (order by a.cv) result
28 from sent2 b join sent1 a on a.id = b.id and a.cv = b.cv and a.word <> b.word
29 group by a.id;
ID RESULT
---------- ------------------------------
1 are flowers chair
SQL>
Column1 =A,B,C,D,E,F
Column2 =C,D,A,F,C,B (It can have duplicates)
I need to remove column2 values from column1 and get the missing value.
Desired output
(Column1)-(Column2) = E
Split columns' contents into rows, use MINUS set operator. Sample data in lines #1 - 3; query begins at line #4.
SQL> with test (col1, col2) as
2 (select 'A,B,C,D,E,F', 'C,D,A,F,C,B' from dual
3 )
4 select regexp_substr(col1, '[^,]+', 1, level) val
5 from test
6 connect by level <= regexp_count(col1, ',') + 1
7 minus
8 select regexp_substr(col2, '[^,]+', 1, level) val
9 from test
10 connect by level <= regexp_count(col2, ',') + 1
11 /
VAL
--------------------------------------------
E
SQL>
If you're comparing columns in a multi-row table, the above approach won't work OK as it'll retrieve duplicates and will be slow. In that case, rewrite it to
SQL> with test (id, col1, col2) as
2 (select 1, 'A,B,C,D,E,F', 'C,D,A,F,C,B' from dual union all
3 select 2, 'A,B,C,D,E,F', 'A,B,B,B' from dual
4 )
5 select id, listagg(val, ',') within group (order by val) missing_letters
6 from
7 (
8 select id,
9 regexp_substr(col1, '[^,]+', 1, column_value) val
10 from test cross join
11 table(cast(multiset(select level from dual
12 connect by level <= regexp_count(col1, ',') + 1
13 ) as sys.odcinumberlist))
14 minus
15 select id,
16 regexp_substr(col2, '[^,]+', 1, column_value) val
17 from test cross join
18 table(cast(multiset(select level from dual
19 connect by level <= regexp_count(col2, ',') + 1
20 ) as sys.odcinumberlist))
21 )
22 group by id;
ID MISSING_LETTERS
---------- --------------------
1 E
2 C,D,E,F
SQL>
You may use translate function with additional cleanup logic to remove all remaining commas. This will work only for single character replacement (one character between commas), but doesn't require to split string into tokens and uses simple string functions.
with a(col1, col2) as (
select 'A,B,C,D,E,F', 'C,D,A,F,C,B' from dual
)
select
/*Then remove leading and trailing commas*/
trim(',' from
/*Then condense all intermediate commas and spaces*/
regexp_replace(
/*Do actual replacement*/
translate(col1, replace(col2, ','), ' '),
'[, ]+', ','
)
) as res
from a
| RES |
| :-- |
| E |
db<>fiddle here
You do not need to split the string.
If your delimited values do not have any characters with special meaning in regular expressions then you can double-up the delimiters in col1 and then convert col2 to a regular expression and replace matches with an empty string and then remove the excess delimiters:
SELECT col1,
col2,
TRIM(
BOTH ',' FROM
REPLACE(
REGEXP_REPLACE(
',' || REPLACE(col1, ',', ',,') || ',',
',(' || REPLACE(col2, ',', '|') || '),'
),
',,',
','
)
) AS missing
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name ( col1, col2 ) AS
SELECT 'A,B,C,D,E,F', 'C,D,A,F,C,B' FROM DUAL UNION ALL
SELECT 'A,AB,BA,B,', 'A,B' FROM DUAL;
Outputs:
COL1
COL2
MISSING
A,B,C,D,E,F
C,D,A,F,C,B
E
A,AB,BA,B,
A,B
AB,BA
If you do have characters with special meaning then you can do a similar replacement using a recursive sub-query:
WITH replacements ( col1, col2 ) AS (
SELECT ',' || REPLACE( col1, ',', ',,') || ',',
col2 || ','
FROM table_name
UNION ALL
SELECT REPLACE(col1, ',' || SUBSTR(col2, 1, INSTR(col2, ','))),
SUBSTR(col2, INSTR(col2, ',') + 1)
FROM replacements
WHERE col2 IS NOT NULL
)
SELECT TRIM(BOTH ',' FROM REPLACE(col1, ',,', ',')) AS missing
FROM replacements
WHERE col2 IS NULL
Which outputs:
MISSING
AB,BA
E
Note: both of these queries only require a single table scan.
db<>fiddle here
Using ora:tokenize you could do something like this (including a few test cases in the with clause; you should remove it, and use your actual table and column names in the main query):
with
inputs (col1, col2) as (
select 'A,B,C,D,E,F', 'C,D,A,F,C,B' from dual union all
select 'D,,F' , 'F,A' from dual union all
select 'A,B,E,F' , 'E' from dual union all
select 'ABC' , 'A,B,ABC' from dual
)
-- END OF TEST DATA; QUERY BEGINS **BELOW THIS LINE**
select i.col1, i.col2, l.diff
from inputs i cross join lateral
( select listagg(token, ',') within group (order by null) as diff
from xmltable('ora:tokenize(.,",")' passing i.col1 || ','
columns token varchar2(10) path '.')
where not ',' || col2 || ',' like '%,' || token || ',%' ) l
;
COL1 COL2 DIFF
----------- ----------- --------------------
A,B,C,D,E,F C,D,A,F,C,B E
D,,F F,A D
A,B,E,F E A,B,F
ABC A,B,ABC
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
I want to select the data from a Oracle table, whereas the table columns contains the data as , [ex : key,value] separated values; so here I want to select the second split i.e, value
table column data as below :
column_data
++++++++++++++
asper,worse
tincher,good
golder
null -- null values need to eliminate while selection
www,ewe
from the above data, desired output like below:
column_data
+++++++++++++
worse
good
golder
ewe
Please help me with the query
According to data you provided, here are two options:
result1: regular expressions one (get the 2nd word if it exists; otherwise, get the 1st one)
result2: SUBSTR + INSTR combination
SQL> with test (col) as
2 (select 'asper,worse' from dual union all
3 select 'tincher,good' from dual union all
4 select 'golder' from dual union all
5 select null from dual union all
6 select 'www,ewe' from dual
7 )
8 select col,
9 nvl(regexp_substr(col, '\w+', 1, 2), regexp_substr(col, '\w+', 1,1 )) result1,
10 --
11 nvl(substr(col, instr(col, ',') + 1), col) result2
12 from test
13 where col is not null;
COL RESULT1 RESULT2
------------ -------------------- --------------------
asper,worse worse worse
tincher,good good good
golder golder golder
www,ewe ewe ewe
SQL>