Pivoting row dynamically in oracle database - oracle

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.

Related

How to generate 18 digit code using cursor based on some conditions in Oracle

I have a table from which I want to generate 18 digit code.
Below is the 18 digit code sample which I want.
R-AP-AP01-SMT-4567
Also for generating the above sample code, here is the data and its logic:
R - Fix value
AP – (2 digit state code from STATE column)
EAST- (From ZONE_NAME column from query below
SMT – (From FORMAT_CODE column from below query)
4567 – (From Store Code column from below query)
SELECT STATE, STORE_CODE, ZONE_NAME FROM TBL_RRSOC_STORE_INFO;
AND
select FORMAT_CODE from TBL_SITE_STORE_FORMAT;
How can it be achieved?
Update
Below is the table description
Table name:- TBL_RRSOC_STORE_INFO
Name Null Type
--------------------------- -------- --------------
RRSOC_ID NOT NULL NUMBER
STORE_CODE NOT NULL NVARCHAR2(55)
STATE NVARCHAR2(55)
SLP_STATE NVARCHAR2(100)
FORMAT_GROUP NVARCHAR2(100)
Table name:- TBL_SITE_STORE_FORMAT
Name Null Type
------------ ---- -------------
ID VARCHAR2(20)
STORE_FORMAT VARCHAR2(100)
ISACTIVE VARCHAR2(3)
FORMAT_GROUP VARCHAR2(100)
FORMAT_CODE VARCHAR2(50)
The way you put it, you'd join those tables somehow (cross join is as good as any other, as you didn't explain it better) and concatenate column values.
Something like this:
SQL> with
2 tbl_rrsoc_store_info (state, store_code, zone_name) as
3 (select 'AP', 'EAST', 'SMT' from dual union all
4 select 'NY', 'WEST', 'XYZ' from dual
5 ),
6 tbl_site_store_format (format_code) as
7 (select 4567 from dual)
8 --
9 select 'R' ||'-'|| r.state ||'-'|| r.store_code ||'-'|| r.zone_name ||'-'|| s.format_code result
10 from tbl_rrsoc_store_info r cross join tbl_site_store_format s;
RESULT
--------------------
R-AP-EAST-SMT-4567
R-NY-WEST-XYZ-4567
SQL>
Function returns a value; you didn't explain how it should look like (which parameters it accepts) so I chose to pass state, presuming it is unique within the table.
Sample data:
SQL> select * From tbl_rrsoc_store_info;
ST STOR ZON
-- ---- ---
AP EAST SMT
NY WEST XYZ
SQL> select * from tbl_site_store_format;
FORMAT_CODE
-----------
4567
Function:
SQL> create or replace function f_test (par_state in varchar2)
2 return varchar2
3 is
4 retval varchar2(18);
5 begin
6 select 'R' ||'-'|| r.state ||'-'|| r.store_code ||'-'|| r.zone_name ||'-'|| s.format_code
7 into retval
8 from tbl_rrsoc_store_info r cross join tbl_site_store_format s
9 where r.state = par_state;
10
11 return retval;
12 end;
13 /
Function created.
Testing:
SQL> select r.state, f_test(r.state) result
2 from tbl_rrsoc_store_info r;
ST RESULT
-- --------------------
AP R-AP-EAST-SMT-4567
NY R-NY-WEST-XYZ-4567
SQL>

Need help in output for Oracle PLSQL function

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.

Using case update in cursor

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 )

Combining two stored procedures into a single one using some joins

I am very new to Oracle; I have written two stored procedures where they have both different parameters. I would like to combine the queries in that two stored procedures into single query in a single stored procedure, and make sure it supports any criteria.
These are the stored procedures:
procedure usp_testsp1(RC1 OUT RCT1,
in_dep_Id IN number,
in_Org_id IN number,
in_emp_no IN number)
is
begin
select emp.Name as name,select emp.phone as phone,select emp.Race as
race
from employee emp
JOIN
Project prj ON
emp.ProjectId=prj.ProjectId
JOIN Project_Org Prorg
ON prj.ProjectId=Prorg.ProjectId
JOIN Organization Org1
ON Prorg.OrgId=Org1.OrgId
JOIN Organization Org2
ON emp.OrgId=Org2.OrgId
AND Org2.OrgType=0
WHERE (upper(emp.emp_No) in (in_emp_No ))
AND Prorg.OrgId=in_Org_id
AND Org2.OrgId=in_dep_Id
end;
procedure usp_testsp2(RC1 OUT RCT1,
in_dep_Id IN number,
in_Org_id IN number,
in_vendor_id IN raw,
in_vendor_startdate IN date,
in_vendor_enddate IN date)
is
begin
OPEN RC1 FOR
select emp.Name as name,select emp.phone as phone,select emp.Race as
race
from
from employee emp
JOIN
Project prj ON
emp.ProjectId=prj.ProjectId
JOIN Project_Org Prorg
ON prj.ProjectId=Prorg.ProjectId
JOIN Organization Org1
ON Prorg.OrgId=Org1.OrgId
JOIN Organization Org2
ON emp.OrgId=Org2.OrgId
AND Org2.OrgType=0
INNER JOIN vendor_Emp
ON emp.employeeid=vendor_emp.employeeid
INNER JOIN Vendor
ON Vendor.VendorId=Vendor_emp.VendorId
WHERE Prorg.OrgId=in_Org_id
AND Org2.OrgId=in_dep_Id
AND Vendor.VendorId=in_vendor_id
AND vendor.StartDate=in_vendor_startdate AND Vendor.EndDate=in_vendor_enddate
end;
My objective is to combine the queries within these two separate procedures into a single query such that if I combine all the parameters, I have in_emp_no, in_vendor_id, in_vendor_startdate, in_vendor_enddate apart from the common parameters any of the parameters can be null and I want to make sure that those things won't affect the rest of the query even though its not null
For ex i have only in_emp_no ='xxxx' and common inputs rest of the params like
in_vendor_id etc is null.
I would like to make my query to work even with single paramter to filter the result set
NOTEWithout using Dynamic SQL
Thanks
BJ
You can make columns in the query behave as optional, as far as your join's go, you may need to play with various combinations to make sure everything works.
Sample Table
create table sqltest (
empid number,
name varchar2(10),
orgid number,
depid number,
vendorid number);
insert into sqltest values(1,'BoB',1,1,21);
insert into sqltest values(2,'Chuck',1,1,21);
insert into sqltest values(3,'Mary',1,2,21);
insert into sqltest values(4,'Jane',1,2,22);
insert into sqltest values(5,'Rick',2,1,22);
insert into sqltest values(6,'Samir',2,6,23);
insert into sqltest values(7,'Kirk',3,6,23);
insert into sqltest values(8,'Alex',3,9,23);
commit;
Sample Proc
create or replace procedure calltest (
rc1 out SYS_REFCURSOR,
pempid number,
porgid number,
pdepid number,
pvendorid number)
is
begin
open rc1 for
select * from sqltest
where
(pempid IS NULL OR empid = pempid) and
(porgid IS NULL OR orgid = porgid) and
(pdepid IS NULL OR depid = pdepid) and
(pvendorid IS NULL OR vendorid = pvendorid);
end;
Sample Call
set autoprint on;
var vcur refcursor;
declare
begin
calltest(rc1=>:vcur,pempid=>NULL,porgid=>1,pdepid=>NULL,pvendorid=>NULL);
--calltest(rc1=>:vcur,pempid=>NULL,porgid=>1,pdepid=>2,pvendorid=>NULL);
--calltest(rc1=>:vcur,pempid=>NULL,porgid=>1,pdepid=>2,pvendorid=>22);
--calltest(rc1=>:vcur,pempid=>NULL,porgid=>NULL,pdepid=>NULL,pvendorid=>23);
end;
Sample Output 1
VCUR
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
EMPID NAME ORGID DEPID VENDORID
--------------------------------------- ---------- --------------------------------------- --------------------------------------- ---------------------------------------
1 BoB 1 1 21
2 Chuck 1 1 21
3 Mary 1 2 21
4 Jane 1 2 22
Sample Output 2
VCUR
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
EMPID NAME ORGID DEPID VENDORID
--------------------------------------- ---------- --------------------------------------- --------------------------------------- ---------------------------------------
3 Mary 1 2 21
4 Jane 1 2 22
Sample Output 3
VCUR
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
EMPID NAME ORGID DEPID VENDORID
--------------------------------------- ---------- --------------------------------------- --------------------------------------- ---------------------------------------
4 Jane 1 2 22
Sample Output 4
VCUR
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
EMPID NAME ORGID DEPID VENDORID
--------------------------------------- ---------- --------------------------------------- --------------------------------------- ---------------------------------------
6 Samir 2 6 23
7 Kirk 3 6 23
8 Alex 3 9 23

How to assign a value to variable from select statement (PL/SQL Developer)

I'm working with PL/SQL Developer.
I'm trying to update values in a column(existing table).
The values used to populate rows should be auto incremented. The starting value is the maximum value that already exist in such field.
An example, I have the following table
ORDER_ID T_NAME T_PRICE
20 CAR 50
NULL VAN 100
NULL BIKE 10
NULL BOAT 300
After running the query I would expect the table to look like:
ORDER_ID T_NAME T_PRICE
20 CAR 50
21 VAN 100
22 BIKE 10
23 BOAT 300
The query I created so far is:
DECLARE
temp_order_id number;
BEGIN
:temp_order_id = SELECT ISNULL(MAX((ORDER_ID)),0) + 1 FROM SALES_ACC;
update SALES_ACC
set (ORDER_ID) = :temp_order_id , :temp_order_id = :temp_order_id + 1
where (ORDER_ID) is null;
END;
Oracle doesn't like assigning a value from select statement to the temp_order_id variable.
Does anyone has any idea how to fix it?
You don't need pl/sql for this - you can do it in a single update statement - eg:
create table test1 as
select 20 order_id, 'CAR' t_name, 50 t_price from dual union all
select null order_id, 'VAN' t_name, 100 t_price from dual union all
select null order_id, 'BIKE' t_name, 10 t_price from dual union all
select null order_id, 'BOAT' t_name, 300 t_price from dual;
update test1
set order_id = (select max(order_id) from test1) + rownum
where order_id is null;
commit;
select * from test1
order by 1;
ORDER_ID T_NAME T_PRICE
---------- ------ ----------
20 CAR 50
21 VAN 100
22 BIKE 10
23 BOAT 300
drop table test1;
As a side note, it sounds like order_id is something that should really be the primary key of the table - if you had that, then you wouldn't be allowed to add a row without a value. Plus, you would also need a sequence that you would then use when inserting data into the table - e.g.:
insert into test1 (order_id, t_name, t_price)
values (test1_seq.nextval, 'TRIKE', 30);
ORACLE's recomended way for this is either:
Create a sequence and a trigger on the table to assign order_id as soon as row is being inserted
or, for Oracle 12c, you can have an IDENTITY column
See How to create id with AUTO_INCREMENT on Oracle?, both approaches are described there.
Within the DECLARE ... BEGIN ... END; section you are in PL/SQL syntax. That is not equal to the SQL syntax. Within PL/SQL syntax you should make use of the so called select into statement.
SELECT ISNULL(MAX((ORDER_ID)),0) + 1
into :temp_order_id
FROM SALES_ACC

Resources