Oracle inserting with different position of attributes - oracle

Please let me know whether Oracle insert statement works if we place the attributes in a different sequence in terms of attribute positions. I am not able to test in dev environment so need expert opinion before I promote changes in PROD directly. Please help.
I am having the following tables:
tableA - col1, col2, col3, col4,col5
tableB - col1, col2, col4, col5
I need to pick the distinct values from tableB and insert into tableA by adding a sequence number to it.
Since distinct and sequence numbers don't work together in insert statements I am using an outer select statement.
Please let me know which of the following 2 options will work ??? If both don't work then please provide your suggestions as well.
option 1 - adding nextval in the outer select statement at the last and keeping col3 as the last position in insert
insert into tableA ( col1, col2, col4, col5, col3 ) select col1, col2, col4, col5, my_seq.nextval as col3 from ( select distinct col1, col2, col4, col5 from tableB );
option 2- adding nextval in the outer select statement in the same sequence and keeping col3 as also in the same position in insert
insert into tableA ( col1, col2, col3, col4, col5 ) select col1, col2, my_seq.nextval as col3, col4, col5 from ( select distinct col1, col2, col4, col5 from tableB );
thanking in advance!!

Both will work. It doesn't matter in which order you insert them, as long as you specify column names (in insert into) and match them in select that follows.
SQL> create sequence my_seq;
Sequence created.
SQL> create table tablea (deptno number, job varchar2(10), seq number);
Table created.
SQL> -- your first query
SQL> insert into tablea (deptno, job, seq)
2 select deptno, job, my_seq.nextval
3 from (select distinct deptno, job from emp);
9 rows created.
SQL> -- your second query
SQL> insert into tablea (seq, job, deptno)
2 select my_seq.nextval, job, deptno
3 from (select distinct deptno, job from emp);
9 rows created.
Result:
SQL> select * from tablea order by seq;
DEPTNO JOB SEQ
---------- ---------- ----------
20 CLERK 1
30 SALESMAN 2
20 MANAGER 3
30 CLERK 4
10 PRESIDENT 5
30 MANAGER 6
10 CLERK 7
10 MANAGER 8
20 ANALYST 9
20 CLERK 10
30 SALESMAN 11
20 MANAGER 12
30 CLERK 13
10 PRESIDENT 14
30 MANAGER 15
10 CLERK 16
10 MANAGER 17
20 ANALYST 18
18 rows selected.
SQL>
Why is it OK? Because there are 9 distinct combinations of [deptno, job] in Scott's EMP table.
SQL> select distinct deptno, job from emp;
DEPTNO JOB
---------- ---------
20 CLERK
30 SALESMAN
20 MANAGER
30 CLERK
10 PRESIDENT
30 MANAGER
10 CLERK
10 MANAGER
20 ANALYST
9 rows selected.
SQL>

Related

How to use a rowcount in select statement to modify the query to fetch data for 10 days , if rowcount is 0 for 5 days?

I need to modify my script using rowcount to check if the data in table or not?. Here, i write the query to select a data for last 5 days from current system date. But sometimes there is no data in table for 5 days. So i need to fetch for 10 day or more.
Query:
Select ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
from JOB_SUMMARY_EXT e, ob_summary s, enterprise ep
where e.id = s.id and e.name_res_key = 'Model'
and s.job_id in (select id from job_summary where
trunc(start_date) > trunc(sysdate) -10 and service_name ='Model2' )
I don't know how to modify my Query using rowcount. If rowcount is 0 then i want select data for 10 days.Otherwise it should to fetch for 5 days automatically. I want this to be done as single query.
It looks that you want to select the last 5 "days" from that table. So, why would you anchor to SYSDATE if there aren't rows for each of those days? I'd suggest another approach: literally, select last 5 days. Here's how.
As I don't have your tables, I'm using Scott's EMP table which contains information about employees. It is an ancient one so HIREDATE column is set to 1980s, but never mind that. Sorting employees by HIREDATE in descending order shows:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select ename, hiredate from emp order by hiredate desc;
ENAME HIREDATE
---------- ----------
ADAMS 12.01.1983 1.
SCOTT 09.12.1982 2.
MILLER 23.01.1982 3.
FORD 03.12.1981 4.
JAMES 03.12.1981 4.
KING 17.11.1981 5. --> I want to fetch rows up to KING
MARTIN 28.09.1981
TURNER 08.09.1981
CLARK 09.06.1981
BLAKE 01.05.1981
JONES 02.04.1981
WARD 22.02.1981
ALLEN 20.02.1981
SMITH 17.12.1980
14 rows selected.
SQL>
As you can see, the 4th date is shared by two employees so I want to include them both. DENSE_RANK analytic function helps:
SQL> with last5 as
2 (select ename,
3 job,
4 sal,
5 hiredate,
6 dense_rank() over (order by hiredate desc) rnk
7 from emp
8 )
9 select ename, job, sal, hiredate
10 from last5
11 where rnk <= 5;
ENAME JOB SAL HIREDATE
---------- --------- ---------- ----------
ADAMS CLERK 1100 12.01.1983
SCOTT ANALYST 3000 09.12.1982
MILLER CLERK 1300 23.01.1982
JAMES CLERK 950 03.12.1981
FORD ANALYST 3000 03.12.1981
KING PRESIDENT 5000 17.11.1981
6 rows selected.
SQL>
What does it do? The LAST5 CTE sorts employees (as above), DENSE_RANK ranks them; finally, the last SELECT (which begins at line #9) fetches desired rows.
In your case, that might look like this:
with last5 as
(select id,
dense_rank() over (order by start_date desc) rnk
from job_summary
where service_name = 'Model2'
)
select ep.enterprise_name,
s.id,
s.submission_date,
e.value
from job_summary_ext e
join ob_summary s on e.id = s.id
join last5 t on t.id = s.id
join enterprise ep on <you're missing join condition for this table>
where e.name_res_key = 'Model';
Note that you're missing join condition for the ENTERPRISE table; if that's really so, no problem - you'd use cross join for that table, but I somehow doubt that you want that.
Finally, as you use SQL*Plus, perhaps you don't need to concatenate all columns and separate them by the pipe | sign - set it as a column separator, e.g.
SQL> set colsep '|'
SQL>
SQL> select deptno, dname, loc from dept;
DEPTNO|DNAME |LOC
----------|--------------|-------------
10|ACCOUNTING |NEW YORK
20|RESEARCH |DALLAS
30|SALES |CHICAGO
40|OPERATIONS |BOSTON
SQL>
If you want to
return 10 last days if select count(*) returns 0, or
return 5 last days if select count(*) returns a positive number
then something like this might help (again based on Scott's EMP table):
with
tcnt as
-- count number of rows; use your own requirement, I'm checking
-- whether someone got hired today. In Scott's EMP table, nobody was
-- so CNT = 0
(select count(*) cnt
from emp
where hiredate >= trunc(sysdate)
)
select e.ename, e.job, e.sal, e.hiredate
from emp e cross join tcnt c
where e.hiredate >= case when c.cnt = 0 then trunc(sysdate) - 10
else trunc(sysdate) - 5
end;
Apply it to your tables; I don't know which of those 3 tables' count you want to check.
Tried to add in comments but it was too long for comments and Not clear on count based on but here is case in where clause substitute your count statement with nvl function
SELECT ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
FROM JOB_SUMMARY_EXT e,
ob_summary s,
enterprise ep
WHERE e.id = s.id
AND e.name_res_key = 'Model'
AND s.job_id IN
(SELECT id
FROM job_summary
WHERE service='Model'
AND trunc(start_date) >
CASE WHEN
(WRITE your SELECT COUNT criteria WITH NVL FUNCTION)<=0 THEN
trunc(sysdate) -10
ELSE trunc(sysdate)-5
END )

SELECT DISTINCT on one column and return multiple other columns in Oracle/TOAD

My goal is to get a distinct for Clm_Pd_Amt column only and return all other columns:
SELECT CLM_AMT, PAID_DATE, MBR, DISTINCT CLM_PD_AMT
FROM MY_CLAIMS
WHERE DATE >= '20200101
AND STATUS = 'CURRENT'
A GROUP BY is equivalent to a distinct, for example, the distinct list of departments in the EMP table can be found with (say)
SQL> select deptno, min(sal)
2 from emp
3 group by deptno
4 /
DEPTNO MIN(SAL)
---------- ----------
30 951
10 1300
20 800
But what if I want to know which employee had that minimum salary. Then you can use the KEEP clause to gather that as well, eg
SQL> select deptno, min(sal), min(empno) KEEP ( dense_rank FIRST order by sal) empno
2 from emp
3 group by deptno
4 /
DEPTNO MIN(SAL) EMPNO
---------- ---------- ----------
10 1300 7934
20 800 7369
30 951 7900
So using that approach, you should be able to adapt your query to get the distinct CLM_PD_AMT and then pick up the other columns with KEEP. This only works if you have a definition for which distinct CLM_PD_AMT means, ie, the smallest? the largest? etc

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 - Is it possible to "set" values inside case statement during update as below?

Is it possible to "set" values inside case statement during update as below ?
UPDATE TABLE1
CASE WHEN COL1 = 'A' THEN SET COL2 = 10, COL3 = 20, COL4 = 30
WHEN COL1 IN ('B','N') THEN SET COL2 = 1, COL3 = 5, COL4 = 7
WHEN COL1 = 'D' THEN SET COL2 = 11, COL3 = 13, COL4 = 17
ELSE SET COL2 = 0, COL3 = 0, COL4 = 0
END;
The corresponding valid syntax would be like this.
UPDATE TABLE1 SET
COL2 = (CASE WHEN COL1 = 'A' THEN 10
WHEN COL1 IN ('B','N') THEN 1
WHEN COL1 = 'D' THEN 11
ELSE 0
END),
COL3 = (CASE WHEN COL1 = 'A' THEN 20
WHEN COL1 IN ('B','N') THEN 5
WHEN COL1 = 'D' THEN 13
ELSE 0
END),
COL4 = (CASE WHEN COL1 = 'A' THEN 30
WHEN COL1 IN ('B','N') THEN 7
WHEN COL1 = 'D' THEN 17
ELSE 0
END);
Looks like you're trying to do a MERGE, with one exception. You can update the table in a single merge statement as follows, except your logic to update all non-matching rows to 0's.
SQL> create table tab1
(
col1 varchar2(10),
col2 number,
col3 number,
col4 number,
merge_flag char(1)
)
Table created.
SQL> insert into tab1 values ('A', 10,11,12,null)
1 row created.
SQL> insert into tab1 values ('B', 20,21,22,null)
1 row created.
SQL> insert into tab1 values ('C', 30,31,32,null)
1 row created.
SQL> commit
Commit complete.
SQL> select * from tab1
COL1 COL2 COL3 COL4 MERGE_FLAG
---------- ---------- ---------- ---------- ----------
A 10 11 12
B 20 21 22
C 30 31 32
3 rows selected.
SQL> merge into tab1 t
using (
select 'A' as col1, 10 as col2, 20 as col3, 30 as col4 from dual
union
select 'B' as col1, 1 as col2, 5 as col3, 7 as col4 from dual
union
select 'N' as col1, 1 as col2, 5 as col3, 7 as col4 from dual
union
select 'D' as col1, 11 as col2, 13 as col3, 17 as col4 from dual
) x
on (t.col1 = x.col1)
when matched then
update set t.col2 = x.col2, t.col3 = x.col3, t.col4 = x.col4, t.merge_flag = 'X'
Merge successfully completed.
SQL> commit
Commit complete.
SQL> select * from tab1
COL1 COL2 COL3 COL4 MERGE_FLAG
---------- ---------- ---------- ---------- ----------
A 10 20 30 X
B 1 5 7 X
C 30 31 32
3 rows selected.
You could run a single update after the merge to change all non matching rows with 0.
In order to do this via case, you would have to repeat the case for each field (as demonstrated by #MaheswaranRavisankar). That's just the way case works. An alternative would be to fabricate a sub-query that provides the same results. While this is longer, it does group related values together, which may be more readable/easier to maintain.
Given the need to set all non-matching values to zero, I would solve that by setting all values to zero first, then updating the matches the appropriate value.
UPDATE table1
SET col2 = 0, col3 = 0, col4 = 0;
UPDATE table1
SET (col2, col3, col4) =
(SELECT a.col2, a.col3, a.col4
FROM (SELECT 'A' AS col1,
10 AS col2,
20 AS col3,
30 AS col4
FROM DUAL
UNION ALL
SELECT 'B' AS col1,
5 AS col2,
5 AS col3,
7 AS col4
FROM DUAL
UNION ALL
SELECT 'N' AS col1,
5 AS col2,
5 AS col3,
7 AS col4
FROM DUAL
UNION ALL
SELECT 'D' AS col1,
13 AS col2,
7 AS col3,
17 AS col4
FROM DUAL) a
WHERE a.col1 = table1.col1);
This also hints at another option: perhaps you should consider creating a table with these values, rather than embedding them in the SQL.

How can i select just one row in Oracle by row id?

I have used mysql database in my application, but I want to migrate to Oracle.
The problem is in that query:
select * from users limit ?,1;"
That query returns every row one by one depending on ?.
How can i do that in oracle?
On Oracle 12c, you could use the row limiting feature using FETCH FIRST clause.
SQL> SELECT empno, sal, deptno FROM emp ORDER BY empno DESC
2 FETCH FIRST 1 ROWS ONLY;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
Prior 12c solution is ROWNUM, however, if you want the row to be first sorted, then you need to do it in a sub-query -
SQL> SELECT empno, sal, deptno FROM
2 ( SELECT * FROM emp ORDER BY empno DESC
3 ) WHERE ROWNUM = 1;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
If the order doesn't matter to you, if you just want any random row, simply use ROWNUM.
Depending on your requirement, you could also use ANALYTIC functions such as ROW_NUMBER, RANK, DENSE_RANK.
select * from (select rownum r, u.* from users u ) where r=1;
or if you want it sorted (replace x by columnnumber or columnname):
select * from (select rownum r, u.* from users u order by x) where r=1;

Resources