Update multiple columns with the value of a single select - oracle

Right now I got something like this:
UPDATE TableA
SET a = (SELECT b FROM TableB),
aa = (SELECT b FROM TableB)
Is there a more elegant solution?

You can write:
UPDATE TableA
SET (a, aa) = (SELECT b, b FROM TableB);
Note: this assumes that TableB contains exactly one row. If not, exception will be raised.

Be careful when updating one table with the values from another, or you will update ALL rows in the target table even if you think you're only updating a few rows. On large tables, this will be very painful (and corrupt your data). For example:
update tab_x x
set (col2, col3) = (
select col2, col3
from tab_y y
where x.col1 = y.col1)
-- the following part will make sure we only update rows
-- that match the join condition (x.col1 = y.col1)
-- without this part, we'd update all rows in the x table!
where exists (
select 1
from tab_y y
where x.col1 = y.col1
)
The full example would be:
SQL> set display on
SQL> drop table tab_x
Table dropped.
SQL> create table tab_x
(
col1 number,
col2 varchar2(20),
col3 varchar2(20)
)
Table created.
SQL> drop table tab_y
Table dropped.
SQL> create table tab_y
(
col1 number,
col2 varchar2(20),
col3 varchar2(20)
)
Table created.
SQL> insert into tab_x values (1, 'Col2 from x','Col3 from x')
1 row created.
SQL> insert into tab_x values (2, 'Col2 from x','Col3 from x')
1 row created.
SQL> insert into tab_x values (3, 'Col2 from x','Col3 from x')
1 row created.
SQL> insert into tab_y values (1, 'Col2 from y','Col3 from y')
1 row created.
SQL> insert into tab_y values (2, 'Col2 from y','Col3 from y')
1 row created.
SQL> insert into tab_y values (9, 'Col2 from y','Col3 from y')
1 row created.
SQL> commit
Commit complete.
SQL> select * from tab_x
COL1 COL2 COL3
---------- -------------------- --------------------
1 Col2 from x Col3 from x
2 Col2 from x Col3 from x
3 Col2 from x Col3 from x
3 rows selected.
SQL> update tab_x x
set (col2, col3) = (
select col2, col3
from tab_y y
where x.col1 = y.col1)
-- the following part will make sure we only update rows
-- that match the join condition (x.col1 = y.col1)
-- without this part, we'd update all rows in the x table!
where exists (
select 1
from tab_y y
where x.col1 = y.col1
)
2 rows updated.
SQL> select * from tab_x
COL1 COL2 COL3
---------- -------------------- --------------------
1 Col2 from y Col3 from y
2 Col2 from y Col3 from y
3 Col2 from x Col3 from x
3 rows selected.

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]+\)', '');

How to create constraint unique multiple columns with specific value for 1 colums in Oracle?

I have already created constraint unique with 5 columns like below :
ALTER TABLE CAMPAIGN_MSISDN add CONSTRAINT ADWISER_UNIQUE UNIQUE (campaign_id,
msisdn, running_date, time_frame, status);
This status has 5 values in the form of enum.
But I want ADWISER_UNIQUE just checking constraint in 2/5 status instead all of status. How can I do it.
Thanks all!
You can use a unique index with the condition. see the following example:
SQL> create table test123 (col1 number, col2 number, col3 number);
Table created.
SQL> -- You need something like this (solution)
SQL> create unique index test123_ix01 on test123(case when col3 in (1,2) then col1 end,
2 case when col3 in (1,2) then col2 end,
3 case when col3 in (1,2) then col3 end);
Index created.
SQL>
Now, Let's check if it works or not:
SQL> insert into test123 values (1,2,3);
1 row created.
SQL> insert into test123 values (1,2,3);
1 row created.
SQL> insert into test123 values (1,2,1);
1 row created.
SQL> insert into test123 values (1,2,2); -- this is of your interest -- see col3 value
1 row created.
SQL> insert into test123 values (1,2,2); -- this is of your interest -- see col3 value
insert into test123 values (1,2,2)
*
ERROR at line 1:
ORA-00001: unique constraint (TEJASH.TEST123_IX01) violated
SQL>
SQL> select * from test123;
COL1 COL2 COL3
---------- ---------- ----------
1 2 3
1 2 3
1 2 1
1 2 2
SQL>
Whoa!!! It restricted multiple values of col3 when col3 is 2 and it will behave the same in case of col3 is 1. all other statuses are allowed to be inserted multiple times.
Cheers!!

Oracle update from random on another table

I have some fields in table1 to update with random values from some fields in table2.
I have to random into rows of table2 and update each rows of table1 with the same rows values of table2.
Here is my SQL code, but it doesn't work.
update owner.table1 t1
set (t1.adress1, t1.zip_code, t1.town) = (select t2.adress, t2.zip_code, t2.town
from table1 t2
where id = trunc(dbms_random.value(1,20000)))
Result: all rows are updated with the same values, like no random on table 2 rows
How about switching to analytic ROW_NUMBER function? It doesn't really create a random value, but might be good enough.
Here's an example: first, create test tables and insert some data:
SQL> create table t1 (id number,address varchar2(20), town varchar2(10));
Table created.
SQL> create table t2 (id number, address varchar2(20), town varchar2(10));
Table created.
SQL> insert into t1
2 select 1, 'Ilica 20', 'Zagreb' from dual union all
3 select 2, 'Petrinjska 30', 'Sisak' from dual union all
4 select 3, 'Stradun 12', 'Dubrovnik' from dual;
3 rows created.
SQL> insert into t2
2 select 1, 'Pavelinska 15', 'Koprivnica' from dual union all
3 select 2, 'Baščaršija 11', 'Sarajevo' from dual union all
4 select 3, 'Riva 22', 'Split' from dual;
3 rows created.
SQL> select * From t1 order by id;
ID ADDRESS TOWN
---------- -------------------- ----------
1 Ilica 20 Zagreb
2 Petrinjska 30 Sisak
3 Stradun 12 Dubrovnik
SQL> select * From t2 order by id;
ID ADDRESS TOWN
---------- -------------------- ----------
1 Pavelinska 15 Koprivnica
2 Baščaršija 11 Sarajevo
3 Riva 22 Split
Update t1 with rows from t2:
SQL> update t1 set
2 (t1.address, t1.town) =
3 (select x.address, x.town
4 from (select row_number() over (order by address) id, t2.address, t2.town
5 from t2
6 ) x
7 where x.id = t1.id);
3 rows updated.
SQL> select * From t1 order by id;
ID ADDRESS TOWN
---------- -------------------- ----------
1 Baščaršija 11 Sarajevo
2 Pavelinska 15 Koprivnica
3 Riva 22 Split
SQL>

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

insert and select in one query default values

I use following query to insert a new row:
insert into table1 (c1, c2, c3) (select c1, c2, c3 from table2 where some_condition)
This works finely if there is a row in table2 that satisfies some_condition. But if there are no rows, nothing is inserted.
Are there any way to specify default values to insert if select returns empty result set? I want to do that in one sql query.
This isn't very pretty, but it does what you want, You'd need to test with your environment to see if it performs well enough
SQL> drop table so_tgt;
Table dropped.
SQL>
SQL> create table so_src (
2 c1 varchar2(6)
3 ,c2 varchar2(6)
4 ,c3 varchar2(6)
5 );
Table created.
SQL>
SQL> insert into so_src values ( 'foo','bar','moo' );
1 row created.
SQL>
SQL> create table so_tgt as select * from so_src where 1 = 0;
Table created.
SQL>
SQL> /* Test for existing row insert */
SQL> insert into so_tgt
2 with x as ( select s.*, 1 as r
3 from so_src s
4 where c1='foo'
5 union
6 select 'x','y','z',0 as r /* DEFAULT VALUES */
7 from dual )
8 select c1,c2,c3
9 from x
10 where r = ( select max(r) from x ) ;
1 row created.
SQL>
SQL> select * from so_tgt;
C1 C2 C3
------ ------ ------
foo bar moo
SQL> truncate table so_tgt;
Table truncated.
SQL>
SQL> /* Test for default row insert */
SQL> insert into so_tgt
2 with x as ( select s.*, 1 as r
3 from so_src s
4 where c1='far'
5 union
6 select 'x','y','z',0 as r /* DEFAULT VALUES */
7 from dual )
8 select c1,c2,c3
9 from x
10 where r = ( select max(r) from x ) ;
1 row created.
SQL>
SQL> select * from so_tgt;
C1 C2 C3
------ ------ ------
x y z
SQL> truncate table so_tgt ;
Table truncated.
The quick and dirty way, if you don't mind repeating some_condition and where some_condition doesn't depend on the values in table2 is:
insert into table1 (c1,c2,c3)
select c1, c2, c3 from table2 where some_condition
union select defaultvalue1, defaultvalue2, defaultvalue3 from dual where not (some_condition)
If some_condition does depend on values in table2, then you can do (untested):
insert into table1 (c1,c2,c3)
select nvl(t2.c1, defaultvalue1), nvl(t2.c2, defaultvalue2), nvl(t2.c2, defaultvalue3)
from dual left join (select c1,c2,c3 from table2 where some_condition) t2
on 1 = 1
If I'm right, this query will always return at least one row, but if no rows showed up on the right side, then the t2 values will all be returned as null and so the nvl can be used to provide your default values.
Edit: Small caveat. This assumes that the values returned from table2 will not be null or that if they are, you want the default values.

Resources