I have a table called employees with the columns:
id
departament_id
salary
and I want to raise the salary by 10% for the employees working in the same department as the employee with id=115, how should I do that?
Here's one option:
Sample data first (based on Scott's EMP table):
SQL> create table test as select deptno, empno id, ename, sal from emp;
Table created.
SQL> select * From test where deptno = 10;
DEPTNO ID ENAME SAL
---------- ---------- ---------- ----------
10 7782 CLARK 2450
10 7839 KING 10000
10 7934 MILLER 1300
SQL>
Let's change salaries for all employees of department 10 - I'll pass Clark's ID as a parameter:
SQL> merge into test t
2 using (select a.deptno
3 from test a
4 where a.id = &par_id
5 ) x
6 on (x.deptno = t.deptno)
7 when matched then update set
8 t.sal = t.sal * 1.1;
Enter value for par_id: 7782
3 rows merged.
Result:
SQL> select * From test where deptno = 10;
DEPTNO ID ENAME SAL
---------- ---------- ---------- ----------
10 7782 CLARK 2695
10 7839 KING 11000
10 7934 MILLER 1430
SQL>
Now that you know how, apply it to your table.
Related
My journey is progressing and I'm learning rapidly. I can't get enough of this stuff... alas I am at a dead end here and need some help.
I am running Visual Studio, am connected to a database (that's filled with dummy data). I am able to run queries on it as expected. However, I'm learning about Procedures right now and I'm coming up with a problem.
I am trying to simply run a query that will select all from table.
CREATE PROCEDURE nearthetop()
BEGIN
SELECT * FROM RESULTS WHERE VOLUME = (SELECT MAX(VOLUME) FROM RESULTS WHERE VOLUME NOT In (SELECT Max(VOLUME) from RESULTS))
END;
When I run this inside Visual Studio I get an error:
EXECUTE FAIL:
CREATE PROCEDURE twofromtop() BEGIN SELECT * FROM RESULTS WHERE VOLUME = (SELECT MAX(VOLUME) FROM RESULTS WHERE VOLUME NOT In (SELECT Max(VOLUME) from RESULTS)) END
Message :
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END' at line 3
When I remove BEGIN and END; from the procedure it creates it fine.
Why is that?
And then, once created, how do I "call" that procedure so as I can see the returned results?
In Oracle, when PL/SQL code uses a SELECT statement, you have to select into something - a local variable, a collection, whatever - or use a cursor loop and deal with cursor variable.
You're selecting row(s) whose volume value is the 2nd largest. Code you wrote works, but it fetches from the same table 3 times which isn't optimal. For example (based on Scott's sample schema), fetching the 2nd largest salary:
SQL> select ename, sal from emp order by sal desc;
ENAME SAL
---------- ----------
KING 5000 --> largest
FORD 3000 --> Ford and Scott both share
SCOTT 3000 --> the 2nd largest salary
JONES 2975
BLAKE 2850
CLARK 2450
<snip>
Your query returns correct result:
SQL> select *
2 from emp
3 where sal = (select max(sal) from emp
4 where sal not in (select max(sal) from emp)
5 );
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7902 FORD ANALYST 7566 03.12.81 3000 20
Consider doing it differently:
SQL> select *
2 from (select e.*,
3 rank() over (order by sal desc) rnk
4 from emp e
5 )
6 where rnk = 2;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO RNK
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20 2
7902 FORD ANALYST 7566 03.12.81 3000 20 2
SQL>
Now, back to your procedure. A simple option which doesn't require much effort is to use a cursor FOR loop. Procedure's IN parameter says which largest salary you want:
SQL> create or replace procedure nearthetop (par_n in number) as
2 begin
3 for cur_r in (select *
4 from (select e.*,
5 rank() over (order by sal desc) rnk
6 from emp e
7 )
8 where rnk = par_n
9 )
10 loop
11 dbms_output.put_line(cur_r.ename ||': '|| cur_r.sal);
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> begin
2 nearthetop(2); --> give me the 2nd largest salary
3 end;
4 /
SCOTT: 3000
FORD: 3000
PL/SQL procedure successfully completed.
SQL> begin
2 nearthetop(5); --> give me the 5th largest salary
3 end;
4 /
BLAKE: 2850
PL/SQL procedure successfully completed.
SQL>
I'm just displaying those values on the screen; you never said what you'd want to do with them.
If you'd like to return the result to the caller, a better option is to use a function instead of a procedure. In this case, it would be a ref cursor it returns.
SQL> create or replace function f_nearthetop (par_n in number)
2 return sys_refcursor
3 as
4 rc sys_refcursor;
5 begin
6 open rc for select *
7 from (select e.*,
8 rank() over (order by sal desc) rnk
9 from emp e
10 )
11 where rnk = par_n;
12 return rc;
13 end;
14 /
Function created.
Testing:
SQL> var l_rc refcursor
SQL>
SQL> exec :l_rc := f_nearthetop(2);
PL/SQL procedure successfully completed.
SQL> print :l_rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO RNK
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20 2
7902 FORD ANALYST 7566 03.12.81 3000 20 2
SQL>
So, yes - there are various options. Which one you'll actually use depends on what you want to do.
(As of Visual Studio: I can't help about it, I don't use it.)
how can a (Oracle)function/Procedure with parameters can be created to return a table, which was returned by a select statement, inside a block
tried with refcursor
getting data as below ,all in a single row
{<G_ID=748,P_G_ID=746,P_NO=null,M_DATE=06-OCT-20 >,
<G_ID=749,P_G_ID=746,P_NO=null,M_DATE=06-OCT-20>} instead of a table format
image link for the sample output
P.S. I am pretty new to PL/SQL
One option is to return refcursor. Here's an example:
SQL> create or replace function f_test (par_deptno in number)
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 open rc for select * from emp where deptno = par_deptno;
7 return rc;
8 end;
9 /
Function created.
SQL> select f_test(10) from dual;
F_TEST(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09.06.81 2450 10
7839 KING PRESIDENT 17.11.81 10000 10
7934 MILLER CLERK 7782 23.01.82 1300 10
SQL>
We have a table in Oracle 11g DB which has millions of records. For a few months, we have noticed that duplicate records are getting inserted into the table. Is there any way to alert via email when the duplicate records get inserted into the table?
We are in the process of creating a unique index for the table, but it will take sometime.
Meanwhile, can an email-alert be created to notify us when duplicate records are getting inserted?
We have OEM installed to monitor this Oracle DB.
If selecting from table takes a long time, I guess you don't want to intercept duplicates as they happen because every insert/update would kill performance even more than it does now.
Therefore, if table isn't in use 24/7, perhaps you could schedule that check and either notify someone or - even better - take action immediately.
That would be a stored procedure, e.g.
create or replace procedure p_del_dup as
begin
delete from your_table a
where a.rowid > (select min(b.rowid)
from your_table b
where b.future_unique_column = a.future_unique_column
);
end;
Live example, based on Scott's sample schema. I'll create a table that contains EMP data, enter some duplicates and delete them.
SQL> create table test as select * from emp where deptno in (10, 20);
Table created.
SQL> create or replace procedure p_del_dup as
2 begin
3 delete from test a
4 where a.rowid > (select min(b.rowid)
5 from test b
6 where b.empno = a.empno --> column(s) which will enforce uniqueness
7 );
8 end;
9 /
Procedure created.
Create a database job; schedule it to run at 02:00 every night
SQL> declare
2 x number;
3 begin
4 dbms_job.submit
5 ( job => x
6 ,what => 'p_del_dup;'
7 ,next_date => to_date('07.04.2020 02:00:00','dd/mm/yyyy hh24:mi:ss')
8 ,interval => 'TRUNC (SYSDATE+1) + 2 / 24'
9 ,no_parse => false
10 );
11 dbms_output.put_line('Job Number is: ' || to_char(x));
12 commit;
13 end;
14 /
Job Number is: 104
PL/SQL procedure successfully completed.
OK, let's now insert some duplicates (all from deptno = 10):
SQL> insert into test select * from emp where deptno = 10;
3 rows created.
SQL> select * from test order by deptno, ename;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09.06.81 2450 10
7782 CLARK MANAGER 7839 09.06.81 2450 10
7839 KING PRESIDENT 17.11.81 5000 10
7839 KING PRESIDENT 17.11.81 5000 10
7934 MILLER CLERK 7782 23.01.82 1300 10
7934 MILLER CLERK 7782 23.01.82 1300 10
7876 ADAMS CLERK 7788 12.01.83 1100 20
7902 FORD ANALYST 7566 03.12.81 3000 20
7566 JONES MANAGER 7839 02.04.81 2975 20
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7369 SMITH CLERK 7902 17.12.80 800 20
11 rows selected.
I won't wait until 02:00 so I'll run the job manually. Remember, its ID is 104?
SQL> exec dbms_job.run(104);
PL/SQL procedure successfully completed.
SQL> select * from test order by deptno, ename;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09.06.81 2450 10
7839 KING PRESIDENT 17.11.81 5000 10
7934 MILLER CLERK 7782 23.01.82 1300 10
7876 ADAMS CLERK 7788 12.01.83 1100 20
7902 FORD ANALYST 7566 03.12.81 3000 20
7566 JONES MANAGER 7839 02.04.81 2975 20
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7369 SMITH CLERK 7902 17.12.80 800 20
8 rows selected.
SQL>
OK, duplicates are silently deleted.
If you want, you can modify the procedure and - using UTL_MAIL package - send an e-mail message to someone. If number of rows deleted is enough, you'd send SQL%ROWCOUNT number. Or, send whatever you want.
Our requirement is if any 1 of the column name in table is updating we need to insert the column name in another table so I had written this code
CREATE OR REPLACE TRIGGER Test AFTER
UPDATE ON XX_table
FOR EACH Row
BEGIN FOR C IN
(SELECT column_name
FROM User_Tab_Columns
WHERE Upper(Table_Name) = 'XX_table_name' ORDER BY column_id ASC)
LOOP
IF Updating (c.column_name)
THEN
INSERT INTO Xx_Trigger_table (Rt_Id ,Updated_Column ,updated_status) VALUES(:Old.Rt_Id,C.Column_Name,'Y');
END IF;
END LOOP;
END;
Now Client need Old value as well as new value in the XX_Trigger_Table.I cant Write
INSERT INTO Xx_Trigger_table (Rt_Id ,Updated_Column ,updated_status,old_value, new_value) VALUES(:Old.Rt_Id,C.Column_Name,'Y',:old.c.column_name,:new.c.column_name);
Please suggest me some idea to insert new and old value in the table.
Thanks in Advance.
If you are following this approach , like #Alex Poole suggested you will have to include all the columns that you would like to log. Please see this procedure and the output of it.
CREATE OR REPLACE TRIGGER Test1 AFTER
UPDATE ON TESTEMP
FOR EACH Row
BEGIN FOR C IN
(SELECT column_name
FROM User_Tab_Columns
WHERE Upper(Table_Name) = 'TESTEMP' ORDER BY column_id ASC)
LOOP
IF Updating (c.column_name) then
IF (c.column_name='EMPNO') then
INSERT INTO test_audit (col_name,old_val,new_val,upd_stat) VALUES (C.column_name,:Old.empno,:New.empno,'Y');
ELSIF
(c.column_name='ENAME') then
INSERT INTO test_audit (col_name,old_val,new_val,upd_stat) VALUES (C.column_name,:Old.ENAME,:New.ENAME,'Y');
ELSIF
(c.column_name='MGR') then
INSERT INTO test_audit (col_name,old_val,new_val,upd_stat) VALUES (C.column_name,:Old.MGR,:New.MGR,'Y');
END IF;
END IF;
END LOOP;
END;
SQL> select * from testemp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
SQL> select * from test_audit;
no rows selected
SQL> update testemp set empno=6677 , ename='JUPITER' where empno=1234;
1 row updated.
SQL> COMMIT;
Commit complete.
SQL> select * from test_audit;
COL_NAME OLD_VAL NEW_VAL U
---------- ---------- ---------- -
EMPNO 1234 6677 Y
ENAME SMITH JUPITER Y
I am new to Oracle. Is there a builtin keyword does the same job of SQL Server APPLY?
I think the equivalent of the APPLY clause in Oracle is called a lateral JOIN. A lateral join in Oracle is when you join a table A with a function F that outputs rows and this function has columns of A as parameters.
Let's build a small example with this setup:
SQL> CREATE OR REPLACE TYPE emp_row AS OBJECT (
2 empno NUMBER(4),
3 ename VARCHAR(10),
4 job VARCHAR(9),
5 deptno NUMBER(2)
6 );
7 /
Type created
SQL> CREATE OR REPLACE TYPE emp_tab AS TABLE OF emp_row;
2 /
Type created
SQL> CREATE OR REPLACE FUNCTION get_emp_dept(p_deptno NUMBER) RETURN emp_tab IS
2 l_result emp_tab;
3 BEGIN
4 SELECT emp_row(empno, ename, job, deptno)
5 BULK COLLECT INTO l_result
6 FROM emp
7 WHERE deptno = p_deptno;
8 RETURN l_result;
9 END get_emp_dept;
10 /
Function created
A lateral join is automatic in Oracle, there is no special keyword:
SQL> SELECT dept.dname, emp.empno, emp.ename, emp.job
2 FROM dept
3 CROSS JOIN TABLE(get_emp_dept(dept.deptno)) emp;
DNAME EMPNO ENAME JOB
-------------- ----- ---------- ---------
ACCOUNTING 7782 CLARK MANAGER
ACCOUNTING 7839 KING PRESIDENT
ACCOUNTING 7934 MILLER CLERK
RESEARCH 7369 SMITH CLERK
RESEARCH 7566 JONES MANAGER
RESEARCH 7788 SCOTT ANALYST
RESEARCH 7876 ADAMS CLERK
RESEARCH 7902 FORD ANALYST
SALES 7499 ALLEN SALESMAN
SALES 7521 WARD SALESMAN
SALES 7654 MARTIN SALESMAN
SALES 7698 BLAKE MANAGER
SALES 7844 TURNER SALESMAN
SALES 7900 JAMES CLERK
14 rows selected
In Oracle we can use a pipelined function in the FROM clause by using the TABLE() function.
SQL> select * from table( get_dept_emps (10) )
2 /
ENAME SAL MGR
------------------------------ ---------- ---------------------
BOEHMER 2450 SCHNEIDER
SCHNEIDER 5000
KISHORE 1300 BOEHMER
SQL>
This can be treated like any other table, for instance, by joining to it:
SQL> select t.*
2 , e.empno
3 from
4 table( get_dept_emps (10) ) t
5 join emp e
6 on e.ename = t.ename
7 /
ENAME SAL MGR EMPNO
---------- ---------- ---------- ----------
BOEHMER 2450 SCHNEIDER 7782
SCHNEIDER 5000 7839
KISHORE 1300 BOEHMER 7934
SQL>
Since 12c, Oracle supports both APPLY and LATERAL natively: https://docs.oracle.com/database/121/NEWFT/chapter12101.htm#FEATURENO10330