single Column Update for multiple rows in oracle10g - oracle

Update table_1 set col1= 1 where col2 = 'ONE';
update table_1 set col1= 2 where col2 = 'TWO';
Update table_1 set col1= 3 where col2 = 'THREE';
...
update table_1 set col1= 100 where col2 = 'HUNDRED';
Is there any simplified way to achive this in a single query instead of writing 100 update statemnets in oracle10g??

I think there might be a solution with Oracle Case-Statement or the decode-function, although it will be a quite long statement and I am not quite sure what the advantage over 100 update statements might be. Also I am not aware of any limitations regarding length of parameter-lists, etc.
Example for Case:
update table_1
set col1 = CASE col2
WHEN 'ONE' THEN 1
WHEN 'TWO' THEN 2
WHEN 'THREE' THEN 3
WHEN 'FOUR' THEN 4
WHEN 'FIVE' THEN 5
WHEN 'SIX' THEN 6
WHEN 'SEVEN' THEN 7
WHEN 'EIGHT' THEN 8
...
WHEN 'HUNDRED' THEN 100
ELSE col2
END;
Example for decode:
update table_1
set col1 = decode(col2,
'ONE', 1,
'TWO', 2,
'THREE', 3,
'FOUR', 4,
'FIVE', 5,
'SIX', 6,
'SEVEN', 7,
'EIGHT', 8,
...
'HUNDRED', 100,
col2);

You could make use of the Julian spelling formats (search asktom.oracle.com for more details)
Here's output from my session:-
create table table_1 (col_1 number, col_2 varchar(20))
insert into table_1 (col_1, col_2) values (null, 'THIRTY-THREE')
insert into table_1 (col_1, col_2) values (null, 'SEVEN')
insert into table_1 (col_1, col_2) values (null, 'EIGHTY-FOUR')
select * from table_1
COL_1 COL_2
THIRTY-THREE
SEVEN
EIGHTY-FOUR
update /*+bypass_ujvc*/
(select t1.col_1, spelled.n
from
table_1 t1
inner join
(select n, to_char(to_date (n, 'J'),'JSP') spelled_n from
(select level n from dual connect by level <= 100)) spelled
on t1.col_2 = spelled.spelled_n
)
set col_1 = n
select * from table_1
COL_1 COL_2
33 THIRTY-THREE
7 SEVEN
84 EIGHTY-FOUR
The nasty hint (bypass_ujvc) ignores the fact that the inline view isn't key preserved - in practice you should use a merge statement instead. But this isn't a real-world scenario, right! (And you'll have to treat your 'HUNDRED' as a special case = 'ONE HUNDRED'.)

Related

how to split one string column of `(12345)some_string` to two column `12345`, `some_string` in Oracle

As the question,
how to split one string column of (12345)some_string to two-column 12345and some_string in Oracle?
Notice: Not all the columns are (12345)some_string, part of columns are only some_string without (12345), the two columns are null and some string
With sample data you posted, this could be one option (line #5 onward):
SQL> with test (col) as
2 (select '(12345)some_string' from dual union all
3 select 'another_string' from dual
4 )
5 select regexp_substr(col, '\d+') col1,
6 substr(col, instr(col, ')') + 1) col2
7 from test;
COL1 COL2
------------------ ------------------
12345 some_string
another_string
SQL>
Assuming the following table:
create table my_table (my_column varchar2(30));
insert into my_table values ('(12345)some_string');
commit;
1) Add a new column to the table
alter table my_table add new_column number;`
2) Fill the new column
update my_table set new_column = regexp_substr(my_column, '^\(([1-9]+)\)', 1, 1, NULL, 1);
3) Update the original column
update my_table set my_column = regexp_replace(my_column, '^\([1-9]+\)', '');

Convert single row result into two column result. Unpivot error 'expression must have same datatype as corresponding expression'

Please read only EDIT section which is more relevant now.
I have table in oracle 12c database, say table_1.
I have to run a simple SQL which returns only 1 row and all columns from from table_1. Say like
select * from table_1 where col_1 = 65
But the result I want is like:
I did some search and found this link which is very similar to what i want.
But I was only able to get the first column in the expected result by folowing it. I am unable to get actual data in the second column of the expected result.
So far I was able to write only:
select v
from (
select 'col_1' as col_1, 'col_2' as col_2, 'col_3' as col_3, 'col_4' as col_4 from dual
) t
unpivot
(
v for val in (col_1,col_2,col_3,col_4)
) u;
How can I add the second column and the condition where col_1 = 65?
EDIT: (Above part is less relevant now)
I fount this link where there us unpivot example which I could use.
select * from olympic_medal_tables
Gives:
desc olympic_medal_tables
Name Null? Type
------------- ----- -----------
NOC VARCHAR2(3)
GOLD_MEDALS NUMBER
SILVER_MEDALS NUMBER
BRONZE_MEDALS NUMBER
Following SQL gives me what I could use, but I don't want the NOC column in the result:
select * from olympic_medal_tables
unpivot (medal_count for medal_colour in (
gold_medals as 'GOLD',
silver_medals as 'SILVER',
bronze_medals as 'BRONZE'
));
Result:
So when I add NOC as well (so it is not added as a column in the result) like:
select * from olympic_medal_tables
unpivot (medal_count for medal_colour in (
gold_medals as 'GOLD',
silver_medals as 'SILVER',
bronze_medals as 'BRONZE',
noc as 'NOC'
));
I get error: ORA-01790: expression must have same datatype as corresponding expression on line 6 Error at Line: 44 Column: 3
I have tried using TO_NUMBER and TO_CHAR as well but then I get various syntax errors.
Question: How can I get expected result with just 2 columns. Is it possible with this approach using unpivot?
Here's one option, which requires you to first select one row, and then apply UNPIVOT to it:
SQL> with test (col1, col2, col3, col4) as
2 (select '12', 'hfkds' , 'hk435k' , '32' from dual union
3 select '34', 'ldkfgj', 'fsjd4653', '324' from dual union
4 select '65', 'ifd' , 'dkfjs345', '23' from dual union
5 select '87', 'dg' , '345jh' , '65' from dual
6 ),
7 one_row as
8 (select * From test
9 where col1 = '65'
10 )
11 select *
12 from one_row
13 unpivot (col_value for col_name in
14 (col1 as 'col1', col2 as 'col2', col3 as 'col3', col4 as 'col4'));
COL_ COL_VALU
---- --------
col1 65
col2 ifd
col3 dkfjs345
col4 23
SQL>

Select default in case of no value returned

I am trying to get some default value in my resultset if query does not return anything. I am trying nvl for the same but it is not returning the expected default value. To simulate, Consider following query,
select nvl(null, '10') from dual where 1=0;
I want to get 10 in case of given condition is not true and query does not return any value. However above query not returning any row.
Your query returns zero rows. NVL() isn't going to change that (*).
The correct solution is for the program which executes the query to handle NO_DATA_FOUND exception rather than fiddling the query.
However, you need a workaround so here is one using two sub-queries, one for your actual query, one to for the default.
When your_query returns an empty set you get this:
SQL> with your_qry as
2 ( select col1 from t42 where 1=0 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
10
SQL>
And when it returns a row you get this:
SQL> with your_qry as
2 ( select col1 from t42 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
12
13
SQL>
The WITH clause is optional here, it just makes it easier to write the query without duplication. This would have the same outcome:
select col1
from t42
where col0 is null
union all
select 10
from dual
where not exists (select col1
from t42
where col0 is null)
;
(*) Okay, there are solutions which use NVL() or COALESCE() with aggregations to do this. They work with single column projections in a single row as this question poses, but break down when the real query has more than one row and/or more than one column. Aggregations change the results.
So this looks alright ...
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual where 1=0 union all
3 select 'X' as col0, 13 as col1 from dual where 1=0 )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Y 10
SQL>
... but this not so much:
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual union all
3 select 'X' as col0, 13 as col1 from dual )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Z 13
SQL>
May be something like this is what you need
You could change WHERE clause (in this case WHERE COL > 1) similarly in both places.
WITH T(COL) AS(
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL
)
SELECT COL FROM T WHERE COL > 1
UNION ALL
SELECT 10 AS COL FROM DUAL WHERE NOT EXISTS( SELECT 1 FROM T WHERE COL > 1)
You can use aggregation. An aggregation query always returns one row:
select coalesce(max(null), '10')
from dual
where 1 = 0;
I prefer coalesce() to nvl() because coalesce() is the ANSI standard function. But, nvl() would work here just as well.

Oracle Sibling Structure

I have a structure that I store equal records in a database table. You can think that these records are siblings. For example I have two records in this table; 1=2 and 1=3. And I need a query that will return all siblings of a given record. Let me give an example;
This is my table with two columns:
create table SIBLINGSTEST(col1 number, col2 number);
I have 2 records, 1=2 and 1=3
insert into SIBLINGSTEST values(1,2);
insert into SIBLINGSTEST values(1,3);
I thought using connect by is the best solution for this situation, and write the following query:
SELECT * FROM SIBLINGSTEST
START WITH (col1 = 1 or col2 = 1)
CONNECT BY NOCYCLE (
(PRIOR col1 = col1) or
(PRIOR col1 = col2) OR
(PRIOR col2 = col1) or
(PRIOR col2 = col2))
This query returns correct results, returning both rows.
If I use 2 as a parameter, the query also runs correctly, returning again both rows.
But if I use 3 as a parameter, the query does not run as I expected, returning only the start row.
SELECT * FROM SIBLINGSTEST
START WITH (col1 = 3 or col2 = 3)
CONNECT BY NOCYCLE (
(PRIOR col1 = col1) or
(PRIOR col1 = col2) OR
(PRIOR col2 = col1) or
(PRIOR col2 = col2))
I wonder why the results of 2 and 3 differs. Any help or idea will be appriciated.
Thanks.
I get both rows with your last query as expected:
SQL> SELECT * FROM SIBLINGSTEST
2 START WITH (col1 = 3 or col2 = 3)
3 CONNECT BY NOCYCLE (
4 (PRIOR col1 = col1) or
5 (PRIOR col1 = col2) OR
6 (PRIOR col2 = col1) or
7 (PRIOR col2 = col2));
COL1 COL2
---------- ----------
1 3
1 2
However I would not choose to model it this way. If what you really want is to record that 1, 2, 3 are siblings then I would use:
create table siblings_group (group_id number);
create table people (person_id number, group_id number);
insert into siblings_group values (1);
insert into people values (1, 1);
insert into people values (2, 1);
insert into people values (3, 1);
Then to find all siblings of 3:
SQL> select person_id from people where group_id =
2 (select group_id from people where person_id=3);
PERSON_ID
----------
1
2
3

How to display comma separated descriptions based on comma separated values in Oracle 10g?

I am new to Oracle technology. Earlier I posted 2 posts for the same issue due to lack of understanding the requirement.
Table 1:
MSGID
-----
1,2,3
2,3
4
null
null
Table 2:
MID MSGDESC
---- -------
1 ONE
2 TWO
3 THREE
4 FOUR
Expected output:
XCOL DESC
----- -----
1,2,3 ONE,TWO,THREE
2,3 TWO,THREE
4 FOUR
I am not able to fulfil this requirement. Please provide me one solution.
Note: tables don't have any unique or primary key values. Table 1 has 5000 records and table 2 only has 80 records with descriptions.
create table Table1 (MSGID varchar2(100));
insert into Table1 values ('1,2,3');
insert into Table1 values ('2,3');
insert into Table1 values ('4');
insert into Table1 values (null);
insert into Table1 values (null);
create table Table2 (MID varchar2(100), MSGDESC varchar2(100));
insert into Table2 values ('1','ONE');
insert into Table2 values ('2','TWO');
insert into Table2 values ('3','THREE');
insert into Table2 values ('4','FOUR');
select
msgid as xcol,
"DESC",
col1, col2, ..., col12
from
Table1
left join (
select
msgid,
wm_concat(msgdesc) as "DESC"
from
(
select
msgid,
msgdesc
from
(select distinct msgid from Table1 where ...)
cross join (
select level as occ from dual connect by level <= 100)
)
left join Table2
on mid = regexp_substr(msgid, '[^,]+', 1, occ)
where
occ <= regexp_count(msgid, ',') + 1
order by msgid, occ
)
group by msgid
) using (msgid)

Resources