I have below 2 tables. One is dept a
DEPT_NO DEPT_NAME DEPT_VALUE
---------- ------------------------------ ----------
10 Chemistry 100
40 Physics 600
20 Mathematics 200
30 Biology 300
50 Cosmos 550
other one is updated_dept b
DEPT DEPT_NAME DEPT_UPDATED_VALUE
---------- ------------------------------ ------------------
10 Chemistry
20 Mathematics
30 Biology
90 Astrology
40 Numerology
50 Cosmos
With the help of the cursor, I want to fetch a.dept_value from 1st table dept a and on basis of (a.dept_no=b.dept and a.dept_name=b.dept_name) update column dept_updated_value in 2nd table updated_dept b using case.
If combination of dept_no and dept_name is not found, I want to update dept_updated_value to 0
I have written below code but it's not giving correct result. Please help
declare
v_dept dept.dept_no%type;
v_dept_name dept.dept_name%type;
v_dept_value dept.dept_value%type;
cursor c_dept_update
is
select dept_no, dept_name, dept_value from dept;
begin
open c_dept_update;
loop
fetch c_dept_update into v_dept,v_dept_name, v_dept_value;
exit when c_dept_update%notfound;
update dept_updated
set dept_updated_value=
case
when dept=v_dept and dept_name=v_dept_name
then v_dept_value
else 0
end;
commit;
end loop;
close c1;
end;
result is like this
DEPT DEPT_NAME DEPT_UPDATED_VALUE
---------- ---------------------------------- ------------------
10 Chemistry 0
20 Mathematics 0
30 Biology 0
90 Astrology 0
40 Numerology 0
50 Cosmos 550
To paraphrase Steven Feuerstein, don't do in a loop what you can do in SQL. So I'd join the tables in an updatable inline view. I haven't tested this so it may need tweaking:
UPDATE ( SELECT b.dept, b.dept_name, b.dept_updated_value, a.dept AS dept2
FROM updated_dept b
LEFT JOIN dept a ON a.dept = b.dept AND a.dept_name = b.dept_name )
SET dept_updated_value = NVL( dept2, 0 )
Related
For each company entry in a table Company, I want to create a random number of rows between 50 and 250 in table Employee in PL/SQL.
Here's one option, based on data in Scott's sample schema.
Departments (that's your company):
SQL> SELECT * FROM dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Target table:
SQL> CREATE TABLE employee
2 (
3 deptno NUMBER,
4 empno NUMBER PRIMARY KEY,
5 ename VARCHAR2 (10),
6 salary NUMBER
7 );
Table created.
Sequence (for primary key values):
SQL> CREATE SEQUENCE seq_e;
Sequence created.
Here's the procedure: for each department, it creates L_ROWS number of rows (line #8) (I restricted it to a number between 1 and 5; your boundaries would be 50 and 250). It also creates random names (line #16) and salaries (line #17):
SQL> DECLARE
2 l_rows NUMBER;
3 BEGIN
4 DELETE FROM employee;
5
6 FOR cur_d IN (SELECT deptno FROM dept)
7 LOOP
8 l_rows := ROUND (DBMS_RANDOM.VALUE (1, 5));
9
10 INSERT INTO employee (deptno,
11 empno,
12 ename,
13 salary)
14 SELECT cur_d.deptno,
15 seq_e.NEXTVAL,
16 DBMS_RANDOM.string ('x', 7),
17 ROUND (DBMS_RANDOM.VALUE (100, 900))
18 FROM DUAL
19 CONNECT BY LEVEL <= l_rows;
20 END LOOP;
21 END;
22 /
PL/SQL procedure successfully completed.
Result:
SQL> SELECT *
2 FROM employee
3 ORDER BY deptno, empno;
DEPTNO EMPNO ENAME SALARY
---------- ---------- ---------- ----------
10 1 ZMO4RFN 830
10 2 AEXL34I 589
10 3 SI6X38Z 191
10 4 59EWI42 397
20 5 DBAMQDA 559
20 6 79X78JV 491
30 7 56ITU5V 178
30 8 09KPAIS 297
30 9 VQUVWDP 446
40 10 AHJZNVJ 182
40 11 0XWI3GC 553
40 12 7GNTCG4 629
40 13 23G871Z 480
13 rows selected.
SQL>
Adapting my answer to this question:
INSERT INTO employees (id, first_name, last_name, department_id)
SELECT employees__id__seq.NEXTVAL,
CASE FLOOR(DBMS_RANDOM.VALUE(1,6))
WHEN 1 THEN 'Faith'
WHEN 2 THEN 'Tom'
WHEN 3 THEN 'Anna'
WHEN 4 THEN 'Lisa'
WHEN 5 THEN 'Andy'
END,
CASE FLOOR(DBMS_RANDOM.VALUE(1,6))
WHEN 1 THEN 'Andrews'
WHEN 2 THEN 'Thorton'
WHEN 3 THEN 'Smith'
WHEN 4 THEN 'Jones'
WHEN 5 THEN 'Beirs'
END,
d.id
FROM ( SELECT id,
FLOOR(DBMS_RANDOM.VALUE(50,251)) AS num_employees
FROM departments
ORDER BY ROWNUM -- Materialize the sub-query so the random values are individually
-- generated.
) d
CROSS JOIN LATERAL (
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= d.num_employees
);
fiddle
I would like to compare two SQL statements which produce the same result. For example
SELECT
id, name,
(SELECT street || ' ' || town from addresses WHERE addresses.id=customers.addressid) AS address
FROM customers;
-- vs
SELECT
id, name,
street || ' ' || town AS address
FROM customers c LEFT JOIN addresses a ON c.id=a.customerid;
Here I want to compare the performance of the subquery compared to using a join. I know about the other advantages and disadvantages of subqueries and joins, but I’m more interested in comparing their performance.
How can I do a performance comparison? Is there some sort of timing test?
Is there some sort of timing test?
Yes, in SQL*Plus, do it as
SQL> set timing on
and then run queries, each of them several times (to avoid caching issues) and compare time needed for each query to complete. Note that on small data sets you won't notice any difference, so - whichever you use, it'll be OK.
SQL> select e.deptno, (select d.dname from dept d where d.deptno = e.deptno) dname, e.ename
2 from emp e
3 where e.deptno = 20;
DEPTNO DNAME ENAME
---------- -------------- ----------
20 RESEARCH SMITH
20 RESEARCH JONES
20 RESEARCH SCOTT
20 RESEARCH ADAMS
20 RESEARCH FORD
Elapsed: 00:00:00.04
SQL> select e.deptno, d.dname, e.ename
2 from emp e join dept d on e.deptno = d.deptno
3 where e.deptno = 20;
DEPTNO DNAME ENAME
---------- -------------- ----------
20 RESEARCH SMITH
20 RESEARCH JONES
20 RESEARCH SCOTT
20 RESEARCH ADAMS
20 RESEARCH FORD
Elapsed: 00:00:00.03
SQL>
Other than that, did you compare explain plans? Did you collect statistics on tables and indexes (and do it regularly)? Are there any indexes (should be on columns used in joins, most probably)?
Am currently working on oracle PLSQL function to list the project numbers, titles and the names of employees who work on each project.
For this function, I am required to obtain an output as such:
[Fragmented example]
1001 Computation: Alvin, Peter
1002 Study methods: Bob, Robert
1003 Racing car: Robert
Here is the output I am currently having.
SQL> execute PROJECTGROUPS;
1001 Computation: Alvin
1001 Computation: Ami
1001 Computation: Michael
1001 Computation: Peter
1001 Computation: Wendy
1002 Study methods: Bob
1002 Study methods: Robert
1003 Racing car: Bob
1003 Racing car: Robert
1004 Football: Douglass
1004 Football: Eadger
1005 Swimming: Douglass
1005 Swimming: Eadger
1006 Training: Aban
1006 Training: Carl
[Current Code]
SQL> set echo on
SQL> set feedback on
SQL> set linesize 100
SQL> set pagesize 200
SQL> set serveroutput on
SQL> --
SQL> -- Task 01
SQL> --
SQL> CREATE OR REPLACE PROCEDURE PROJECTGROUPS
2 IS
3 Previous_pnum PROJECT.P#%type := -1;
4 --
5 begin
6 for currentRow IN(select p.P#, p.PTITLE, e.NAME
7 from PROJECT p LEFT OUTER JOIN EMPLOYEE e
8 on p.d# = e.d#
9 WHERE p.P# IN(1001,1002,1003,1004,1005,1006)
10 ORDER BY p.P#, p.PTITLE, e.NAME)
11 --
12 --
13 loop
14 if currentRow.P# is not null then
15 dbms_output.put_line(currentRow.P# || ' ' || currentRow.PTITLE || ': ' || currentRow.NAME);
16 end if;
17 Previous_pnum := currentRow.P#;
18 end loop;
19 dbms_output.put_line(NULL);
20 END;
21 /
Procedure created.
What you need is listagg function.
Here is the SQL -
SELECT p.P#
,p.PTITLE
,listagg(e.NAME, ',') within group (order by e.name) employee_names
FROM PROJECT p
,EMPLOYEE e
WHERE p.P# IN(1001,1002,1003,1004,1005,1006)
AND p.d# = e.d#(+)
GROUP
BY p.P#
,p.PTITLE
ORDER
BY p.P#
,p.PTITLE
LISTAGG() function to return the string in order to print directly with alias (str), and print such as DBMS_OUTPUT.PUT_LINE( str ) :
SELECT p.p#||' '||p.ptitle||': '||LISTAGG( e.name, ',' ) WITHIN GROUP ( ORDER BY e.name ) AS str
FROM project p
LEFT JOIN employee e
ON p.d# = e.d#
WHERE p.p# BETWEEN 1001 AND 1006
GROUP BY p.p#, p.ptitle
ORDER BY p.p#, p.ptitle;
we don't know your sample data set. Perhaps you'd need INNER JOIN depending on the data. Btw, it seems need to fix the data structure of project table. The column d# should be moved to another(the third) table I think.
Can you help me to pivot the details in my Oracle table PAY_DETAILS
PAY_NO NOT NULL NUMBER
EMP_NO NOT NULL VARCHAR2(10)
EMP_ERN_DDCT_NO NOT NULL VARCHAR2(21)
ERN_DDCT_CATNO NOT NULL VARCHAR2(10)
ERN_DDCT_CATNAME NOT NULL VARCHAR2(1000)
PAY_MONTH NOT NULL DATE
AMOUNT NOT NULL NUMBER(10,2)
EARN_DEDUCT NOT NULL VARCHAR2(2)
select EMP_NO,EMP_ERN_DDCT_NO,AMOUNT,EARN_DEDUCT, ERN_DDCT_CATNO from pay_details
EMP_NO EMP_ERN_DDCT_NO AMOUNT EA ERN_DDCT_C
---------- --------------------- ---------- -- ----------
219 10 175 A 001
219 1 5000 A 002
794 7 50000 A 001
769 6 35000 A 001
465 4 5000 A 002
289 2 5000 A 002
435 3 5000 A 002
816 38 5 D 201
737 30 5 D 201
Is it possible to make this output into a cross tab?
So, lets assume you want to pivot your salary data for each month. You can use the following query.
SELECT * FROM
(
SELECT emp_no,
emp_ern_ddct_no,
pay_month,
amount
FROM pay_details
)
PIVOT
(
SUM(amount)
FOR pay_month IN ('01/01/2016', '02/01/2016', '03/01/2016','04/01/2016','05/01/2016','06/01/2016','07/01/2016','08/01/2016','09/01/2016','10/01/2016','11/01/2016','12/01/2016')
)
ORDER BY emp_no;
This is just an example, you can PIVOT your data based on different columns. For more details refer to the following link.
http://www.techonthenet.com/oracle/pivot.php
http://www.oracle-developer.net/display.php?id=506
Since you are on Oracle 10g PIVOT wont work. Try using something similar to the below query.
SELECT emp_no,
SUM(CASE WHEN pay_month ='01/01/2016' THEN AMOUNT ELSE 0 END) jan_pay,
SUM(CASE WHEN pay_month ='02/01/2016' THEN AMOUNT ELSE 0 END) feb_pay,
SUM(CASE WHEN pay_month ='03/01/2016' THEN AMOUNT ELSE 0 END) march_pay
.........
FROM pay_details
GROUP BY emp_no;
below is the scenario:
(* denotes primary key)
**CompanyUnit**(*Unit_id, unit_loc,Year);
**Employees**(Unit_id,Dept_id,no_of_emp); (unit_id and dept_id are foreign keys)
**ref_department**(*dept_id,dept_name,dept_desc);
sample data:
unit_id unit_loc year
------------------------------------
1 Delhi 2003
2 Mumbai 2004
------------------------------------
dept_id dept_name dept_desc
----------------------------------------
101 ABC ABC-AI
102 ABC ABC-BI
103 ABC ABC-CS
104 XYZ XYZ-Testing
105 XYZ XYZ-Development
----------------------------------------------
unit_id dept_id no_of_emp
----------------------------------------------
1 101 5000
2 102 3000
1 103 4000
1 104 2000
2 105 1000
2 101 3000
----------------------------------------------
Required output: A dynamic view or select:
---------------------------------------------------------------------------
unit_id unit_loc ABC-AI ABC-BI ABC-CS XYZ-Testing XYZ-Development
---------------------------------------------------------------------------
1 Delhi 5000 4000 2000
2 Mumbai 3000 3000 1000
---------------------------------------------------------------------------
The problem is that each new department in ref_department corresponds to a new column in view/select query.
I've written below query:
variable DEPARTMENTS VARCHAR2(100)
BEGIN
:DEPARTMENTS :=NULL;
FOR cur_rec IN (SELECT DISTINCT DEPT_DESC FROM REF_DEPARTMENT) LOOP
:DEPARTMENTS := :DEPARTMENTS || ''''|| cur_rec.DEPT_DESC|| '''' || ',' ;
END LOOP;
dbms_output.put_line(rtrim(:DEPARTMENTS,','));
select * from
(select uid,uloc,uyear, depdesc, emp from
(select C.unit_id as uid, C.unit_loc as uloc,C.year as uyear, E.DEPT_ID as depid,R.DEPT_NAME as depname,R.DEPT_DESC as depdesc,S.NO_OF_EMP as emp
FROM Unit U INNER JOIN Employees E ON U.Unit_id=E.unit_id INNER JOIN REF_DEPARTMENT R ON E.DEPT_ID=R.DEPT_ID))
pivot
(min(emp) for depdesc in (:departments));
END;
error: PL/SQL: ORA-56901: non-constant expression is not allowed for pivot|unpivot
I've referred below links:
Pivoting rows into columns dynamically in Oracle;
http://www.orafaq.com/forum/t/187172/
Long ago (before Oracle's PIVOT queries existed) I wrote a blog post on "Pivot" Queries that gives you the code for a package to help construct such queries.
For your requirement you would do something like this:
declare
rc sys_refcursor;
begin
rc := pivot.pivot_cursor
( group_cols => 'u.unit_id, u.unit_loc'
, pivot_col => 'rd.dept_desc'
, tables => 'CompanyUnit x, Employees e, ref_departmnent rd'
, value_cols => 'e.no_of_emp'
, agg_types => 'sum'
, where_clause => 'e.dept_id = rd.dept_id and e.unit_id = c.unit_id'
);
end;
/
Since the ref cursor can have a variable number of columns you would need to use the DBMS_SQL package to parse it, find out the column names, and run it. The starting point would be to convert the ref cursor to a DBMS_SQL cursor:
n := dbms_sql.to_cursor_number(rc);
You'd need to refer to the DBMS_SQL package documentation example to take this further.