changing how oracle output results - plsql

please read carefully and if you do not understand what I am saying please let me know.
Below are the tables names and the columns in those tables
table: SYSTEM_USER_SKILL
user_id
skill_id
user_abilities
table: SYSTEM_ADMIN_SKILL
skill_id
skill_name
table: SYSTEM_USER
user_id
user_full_name
I want to write a dynmanic sql or a pl/sql which will display the user_full_name and user_ability.
but I want it to display like this:
p.s. where you see the "Y" and "N" those are results from user_abilities
I dont know how to do this all i know is that it can be done with dynamic sql or pl/sql

Getting the data in tabular form can be done with a pivot, as long as you're on Oracle 11gR2:
select * from (
select su.user_full_name, sas.skill_name, sus.user_abilities
from system_user su
cross join system_admin_skill sas
left join system_user_skill sus on sus.user_id = su.user_id
and sus.skill_id = sas.skill_id
) pivot (max(user_abilities) as able for (skill_name)
in ('php' as php, 'java' as java, 'pl/sql / sql' as plsql_sql,
'Oracle apex' as oracle_apex));
USER_FULL_NAME PHP_ABLE JAVA_ABLE PLSQL_SQL_ABLE ORACLE_APEX_ABLE
-------------------- -------- --------- -------------- ----------------
Sarah woods N N Y Y
John brown N Y Y Y
Johnny paterson Y Y Y Y
Amy brown N N Y N
but you need to list each skill explicitly in the in clause. SQL Fiddle.
In SQL*Plus or SQL Developer you could get the headings you want with something like:
column user_full_name heading "Name"
column php_able format a15 heading "php"
column java_able format a15 heading "java"
column plsql_sql_able format a15 heading "pl/sql / sql"
column oracle_apex_able format a15 heading "Oracle apex"
select ...
Name php java pl/sql / sql Oracle apex
-------------------- --------------- --------------- --------------- ---------------
Sarah woods N N Y Y
John brown N Y Y Y
Johnny paterson Y Y Y Y
Amy brown N N Y N
... but I have no idea if Toad has anything similar.
The non-pivot equivalent of this, which you would have had to use in older versions, would be something like:
select su.user_id, su.user_full_name,
max(case when sas.skill_id = 1 then sus.user_abilities end) as php,
max(case when sas.skill_id = 2 then sus.user_abilities end) as java,
max(case when sas.skill_id = 3 then sus.user_abilities end) as plsql_sql,
max(case when sas.skill_id = 4 then sus.user_abilities end) as oracle_apex
from system_user su
cross join system_admin_skill sas
left join system_user_skill sus on sus.user_id = su.user_id
and sus.skill_id = sas.skill_id
group by su.user_id, su.user_full_name;
This still has all the skills hard-coded in. To allow for additional skills without having to modify the query you'd need to build it dynamically, as shown in earlier answers linked to in comments. In this specific case you could do something like:
create or replace function get_skill_matrix return sys_refcursor as
query varchar2(32767);
rc sys_refcursor;
begin
query := 'select su.user_id, su.user_full_name';
for tmp in (select skill_id, skill_name from system_admin_skill
order by skill_id)
loop
query := query || ', max(case when sas.skill_id = ' || tmp.skill_id
|| ' then sus.user_abilities end) as "'
|| substr(tmp.skill_name, 1, 30) || '"';
end loop;
query := query || ' from system_user su';
query := query || ' cross join system_admin_skill sas';
query := query || ' left join system_user_skill sus on sus.user_id = su.user_id';
query := query || ' and sus.skill_id = sas.skill_id';
query := query || ' group by su.user_id, su.user_full_name';
open rc for query;
return rc;
end;
/
The query string used for the dynamic SQL is just built up to look exactly the same as the static version, but with each skill_id-specific clause generated in a loop from the base system_admin_skill table.
When you call this function you get a cursor back. You haven't really said how or where you're using the results. You can treat it as a result set in a Java program, for example, by executing the function and then calling getCursor() on the statement object. In SQL*Plus or SQL Developer you can use the variable and print commands to display it:
var rc refcursor;
exec :rc := get_skill_matrix;
print rc
In SQL Developer that gives:
anonymous block completed
RC
----------------------------------------------------------------------------------------------
USER_ID USER_FULL_NAME java php pl/sql / sql Oracle apex
--------------------------------------- -------------------- ---- --- ------------ -----------
3 Amy brown N N Y N
4 Sarah woods N N Y Y
2 Johnny paterson Y Y Y Y
1 John brown Y N Y Y
Or in SQL*Plus, where you can do column user_id noprint to hide the ID (which I've included in case two people have the same name!):
Name j p p O
-------------------- - - - -
Amy brown N N Y N
Sarah woods N N Y Y
Johnny paterson Y Y Y Y
John brown Y N Y Y
In Toad I think you can do this, which is all the exec is doing anyway:
begin
:rc := get_skill_matrix;
end;
... and it'll prompt you for the bind type for :rc; if you pick 'ref cursor' it'll show the results in the data grid. Apparently.
OK, same thing but without a function (but still PL/SQL), and including the skill ID in the column alias before truncating to make it unique:
var rc refcursor
declare
query varchar2(32767);
begin
query := 'select su.user_id, su.user_full_name as "Name"';
for tmp in (select skill_id, skill_name from system_admin_skill
order by skill_id) loop
query := query || ', max(case when sas.skill_id = ' || tmp.skill_id
|| ' then sus.user_abilities end) as "'
|| substr(tmp.skill_id || ' ' || tmp.skill_name, 1, 30) || '"';
end loop;
query := query || ' from system_user su';
query := query || ' cross join system_admin_skill sas';
query := query || ' left join system_user_skill sus on sus.user_id = su.user_id';
query := query || ' and sus.skill_id = sas.skill_id';
query := query || ' group by su.user_id, su.user_full_name';
open :rc for query;
end;
/
print rc
Which gives:
RC
-----------------------------------------------------------------------------
USER_ID Name 1 java 2 php 3 pl/sql / sql 4 Oracle apex
-------------- -------------------- ------ ----- -------------- -------------
3 Amy brown N N Y N
4 Sarah woods N N Y Y
2 Johnny paterson Y Y Y Y
1 John brown Y N Y Y

Related

join the values of two strings in a single loop

I have below Two Strings
Src Id = 1:2:3:4
Src Qty = 2:4:7:9
So I want to Insert into a Table like
Insert into tbl (Src_id,qty) values (1,2);
Insert into tbl (Src_id,qty) values (2,4);
Insert into tbl (Src_id,qty) values (3,7);
and so on for all
So How i can use a loop by using APEX_UTIL.STRING_TO_TABLE or something else to align the Src id and Qty to insert them as single row in a table
You don't need a loop (i.e. PL/SQL); everything can be done in SQL. Here's how:
SQL> with test (src_id, src_qty) as
2 (select '1:2:3:4', '2:4:7:9' from dual)
3 select regexp_substr(src_id, '[^:]+', 1, level) ||','||
4 regexp_substr(src_qty, '[^:]+', 1, level) result
5 from test
6 connect by level <= regexp_count(src_id, ':') + 1;
RESULT
---------------------------------------------------------
1,2
2,4
3,7
4,9
SQL>
I have done the Solution as below
DECLARE
l_src_arr2 APEX_APPLICATION_GLOBAL.VC_ARR2;
l_qty_arr2 APEX_APPLICATION_GLOBAL.VC_ARR2;
BEGIN
l_src_arr2 := APEX_UTIL.STRING_TO_TABLE ('1:2:3:4');
l_qty_arr2 := APEX_UTIL.STRING_TO_TABLE ('2:4:7:9');
FOR i IN 1 .. l_src_arr2.COUNT LOOP
DBMS_OUTPUT.PUT_LINE ('Src = ' || l_src_arr2 (i) || ' qty = ' || l_qty_arr2 (i));
END LOOP;
END;

substr instr length for loop

good morning guys i have a problem with code i work on Health Care and complain code must be checkbox but they ask for Report that contain the treatment code which is gonna appear in database like this 1:15:2:3 etc so i need to calculate each code separate
i have to count until i get ":" then i need to take the number which can be 1 or 2 digit then making inner join with the other table
can anyone help me to fix this function and the problem in the loop and get the number for each one
create or replace function hcc_get_tcd_codes (p_id in number )
return varchar2 is
x number := 0 ;
y number := 0 ;
z number ;
code1 number ;
code_name varchar2(15);
begin
for i in 0 .. x
loop
select length(t.tcd_codes ) into x from hcc_patient_sheet t where t.id = p_id ; --- (9)العدد كامل
select instr(tcd_codes, ':') into y from hcc_patient_sheet t where t.id = p_id ; ---- عدد الكود الاو(3)ل
select instr(tcd_codes, ':')+1 + y into z from hcc_patient_sheet t where t.id = p_id ; --عدد الكود كامل +1
enter code here
i := x -y ;
select substr(t.tcd_codes,z, instr(tcd_codes, ':')-1) into code1
--,select substr(t.tcd_codes, 0, instr(tcd_codes, ':')-1) as code2
from Hcc_Patient_Sheet t
where t.id = 631 ;
select t.alt_name into code_name from hcc_complaint_codes t where t.code = code1 ;
select instr(tcd_codes, ':') into y from hcc_patient_sheet t where t.id = p_id ; ---- عدد الكود الاول
return code_name ;
end loop ;
end;
Often with frequent sounding string processing issues, a wheel has already been invented, and even packaged.
select * from table(apex_string.split('THIS:IS:GREAT',':'));
Partial SUBSTR doesn't seem to be the best option; I'd suggest you to split that colon-separated-values string into row as follows:
SQL> with test (col) as
2 (select '1:15:2:3' from dual)
3 select regexp_substr(col, '[^:]+', 1, level) one_value
4 from test
5 connect by level <= regexp_count(col, ':') + 1;
ONE_VALUE
--------------------------------
1
15
2
3
SQL>
and use such an option in your query; something like this:
select ...
into ...
from some_table t
where t.id in (select regexp_substr(that_string, '[^:]+', 1, level) one_value
from dual
connect by level <= regexp_count(that_string, ':') + 1
);
If it has to be row-by-row, use the above option as a source for the cursor FOR loop, as
for cur_r in (select regexp_substr(that_string, '[^:]+', 1, level) one_value
from dual
connect by level <= regexp_count(that_string, ':') + 1
)
loop
do_something_here
end loop;

PL/SQL convert rows to grouped columns

I'm not sure how to phrase this question, but I am trying to convert a row value into columns in PL/SQL. I'm not a newbie, but I just can't figure how to go about doing this. I have the following table data:
Name Responsibility
Joe Sales
Steve Sales
Paul Exec
Pete Manager
John Exec
Roger Sec
Scott Exec
I need to create a query that will return and display the data as follows:
Sales Manager Exec Sec
Joe Pete John Roger
Steve Null Scott Null
Null Null Paul Null
The problem I am facing is that the row data is not related to eachother in anyway. Can this even be done?
I appreciate any help anyone can offer on this.
Thanks.
First of all it would be better if you give us your database version in the future.For exemple if you have Oracle 11g even if it isn't exectly what you want you can do something similar with PIVOT
with pivot_data as (select rownum as no,emp_name,job_name from (
select 'Joe' as emp_name,'Sales' as job_name from dual union all
select 'Steve','Sales' from dual union all
select 'Paul','Exec' from dual union all
select 'Pete','Manager' from dual union all
select 'John','Exec' from dual union all
select 'Roger','Sec' from dual union all
select 'Scott','Exec' from dual))
select "'Sales'" as Sales,"'Exec'" as Exec,"'Sec'" as Sec,"'Manager'" as Manager
from pivot_data
pivot(max(emp_name) for job_name in ('Sales','Exec','Sec','Manager'))
SALES EXEC SEC MANAGER
-------------------------------
Roger
Scott
John
Joe
Steve
Paul
Pete
Or if this isn't enough for you this PL/SQL does what you want.I tested it on 11g and i'm fairly certain that it would work with 10g et 9i but i can't guarantee anything for DB2 or PostGre :
declare
cursor C1 is
select 'Joe' as emp_name,'Sales' as job_name from dual union all
select 'Steve','Sales' from dual union all
select 'Paul','Exec' from dual union all
select 'Pete','Manager' from dual union all
select 'John','Exec' from dual union all
select 'Roger','Sec' from dual union all
select 'Scott','Exec' from dual;
varray_sales dbms_sql.varchar2_table;
varray_exec dbms_sql.varchar2_table;
varray_manager dbms_sql.varchar2_table;
varray_sec dbms_sql.varchar2_table;
i_sales number := 0;
i_exec number := 0;
i_manager number := 0;
i_sec number := 0;
n_line_aextr number;
colwidth number := 30;
line varchar2(120);
function max_de_deux (i number,x number) return number is
begin
return case when i > x then i else x end;
end;
begin
dbms_output.enable(120);
for l1 in c1 loop
if L1.job_name = 'Sales' then
i_sales := i_sales+1;
varray_sales(i_sales) := L1.emp_name;
elsif L1.job_name = 'Exec' then
i_exec := i_exec+1;
varray_exec(i_exec) := L1.emp_name;
elsif L1.job_name = 'Manager' then
i_Manager := i_Manager+1;
varray_manager(i_Manager) := L1.emp_name;
elsif L1.job_name = 'Sec' then
i_sec := i_sec+1;
varray_sec(i_sec) := L1.emp_name;
end if;
end loop;
dbms_output.put_line(rpad('Sales',colwidth,' ')||rpad('Exec',colwidth,' ')||rpad('Manager',colwidth,' ')
||rpad('Sec',colwidth,' '));
dbms_output.put_line(rpad('-',colwidth,'-')||rpad('-',colwidth,'-')||rpad('-',colwidth,'-')
||rpad('-',colwidth,'-'));
n_line_aextr := max_de_deux(i_manager,max_de_deux(i_sec,max_de_deux(i_sales,i_exec)));
for l in 1..n_line_aextr loop
line := '';
if l <= i_sales then
line := line || rpad(varray_sales(l),colwidth,' ');
else
line := line || rpad(' ',colwidth,' ');
end if;
if l <= i_exec then
line := line || rpad(varray_exec(l),colwidth,' ');
else
line := line || rpad(' ',colwidth,' ');
end if;
if l <= i_manager then
line := line || rpad(varray_manager(l),colwidth,' ');
else
line := line || rpad(' ',colwidth,' ');
end if;
if l <= i_sec then
line := line || rpad(varray_sec(l),colwidth,' ');
else
line := line || rpad(' ',colwidth,' ');
end if;
dbms_output.put_line(line);
end loop;
end;
Sales Exec Manager Sec
------------------------------------------------------------
Joe Paul Pete Roger
Steve John
Scott

Pl/SQL - oracle 9i - Manual Pivoting

We have a table which has three columns in it:
Customer_name, Age_range, Number_of_people.
1 1-5 10
1 5-10 15
We need to return all the number of people in different age ranges as rows of a single query. If we search for customer #1, the query should just return one row:
Header- Age Range (1-5) Age Range (5-10)
10 15
We needed to get all the results in a single row; When I query for customer 1, the result should be only number of people in a single row group by age_range.
What would be the best way to approach this?
You need to manually perform a pivot:
SELECT SUM(CASE WHEN age_range = '5-10'
THEN number_of_people
ELSE NULL END) AS nop5,
SUM(CASE WHEN age_range = '10-15'
THEN number_of_people
ELSE NULL END) AS nop10
FROM customers
WHERE customer_name = 1;
There are easy solutions with 10g and 11g using LISTGAGG, COLLECT, or other capabilities added after 9i but I believe that the following will work in 9i.
Source (http://www.williamrobertson.net/documents/one-row.html)
You will just need to replace deptno with customer_name and ename with Number_of_people
SELECT deptno,
LTRIM(SYS_CONNECT_BY_PATH(ename,','))
FROM ( SELECT deptno,
ename,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) -1 AS seq
FROM emp )
WHERE connect_by_isleaf = 1
CONNECT BY seq = PRIOR seq +1 AND deptno = PRIOR deptno
START WITH seq = 1;
DEPTNO CONCATENATED
---------- --------------------------------------------------
10 CLARK,KING,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD
3 rows selected.
This will create a stored FUNCTION which means you can access it at any time.
CREATE OR REPLACE FUNCTION number_of_people(p_customer_name VARCHAR2)
RETURN VARCHAR2
IS
v_number_of_people NUMBER;
v_result VARCHAR2(500);
CURSOR c1
IS
SELECT Number_of_people FROM the_table WHERE Customer_name = p_customer_name;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_number_of_people;
EXIT WHEN c1%NOTFOUND;
v_result := v_result || v_number_of_people || ' ' || CHR(13);
END;
END;
To run it, use:
SELECT number_of_people(1) INTO dual;
Hope this helps, and please let me know if there are any errors, I didn't testrun the function myself.
Just do
select Number_of_people
from table
where Customer_name = 1
Are we missing some detail?

T-Sql help needed in Sql Server 2005

I have created one stored procedure which runs on 5000 users in tbluser table with some filter condition in database.There are 4 filtering condition(FC1,FC2,FC3,FC4).Filtering condition has some ListBox and dropdown list of department and countries.I want output as given below:
ID Name StaffNo department Points
1 KK 111 dep1 2
2 NN 222 dep2 1
3 DD 333 dep3 4
I got ID,Name,StaffNo,department in resultset but not points.
points calculation would be based on filtering condition like
if FC1 matched user gained point 1,if both FC1 and FC2 matched user gained 2 point,if both FC1 ,FC2 and FC3 matched user gained 3 point etc.
--in stored procedure i m using dynamic query
DECLARE #SQL VARCHAR(2000)
SET #SQL = 'SELECT U.UserID, U.StaffNo,U.FirstName+'' ''+ U.LastName AS EmployeeName,''?'' AS Points FROM tblUser U '
SET #SQL = #SQL+' WHERE U.Department in (' + #SqlDepartment + ') '
---------------------Update---------------------------------------
IF #SqlLanguage <> ''
SET #SQL = #SQL+' OR U.UserID IN (SELECT UserID FROM Country WHERE LCValues IN ('+ #SqlLanguage +') )'
IF #SqlAreas <> ''
SET #SQL = #SQL+' OR U.UserID IN (SELECT UserID FROM tblAreas WHERE '+#SqlAreas+')'
---------------------Update---------------------------------------
...other filtering condition
EXEC (#SQL)
all filtering condition are implemented with OR logic.
Have you tried implementing a CASE statement to calculate the "Points"?
--in stored procedure i m using dynamic query
DECLARE #SQL VARCHAR(2000)
SET #SQL = '
SELECT
U.[UserID],
U.[StaffNo],
U.[FirstName]+'' ''+ U.[LastName] AS EmployeeName,
(
CASE WHEN EXISTS(SELECT 1 FROM [Country] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [tblAreas] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [OtherTable1] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [OtherTable2] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END
) AS Points
FROM [tblUser] U'
SET #SQL = #SQL+' WHERE U.Department in (' + #SqlDepartment + ') OR'...
...other filtering condition
EXEC (#SQL)
I think you might be better off working out the points in your code rather than in the SQL
The only way I can think of doing it is with UNIONS which would be horrible
You can use left outer join to your filter condition, group by UserID to not get duplicates, add a value of 1 for hits and use coalesce to set 0 for no hits.
Some sample code that shows what I mean using Area and Country as filter condition.
declare #U table (UserID int, Name varchar(50), StaffNo char(3), department char(4))
declare #C table (CountryID int, UserID int)
declare #A table (AreaID int, UserID int)
insert into #U values (1, 'KK', '111', 'dep1')
insert into #U values (2, 'NN', '222', 'dep2')
insert into #U values (3, 'DD', '333', 'dep3')
insert into #C values(1, 1)
insert into #C values(2, 1)
insert into #C values(3, 2)
insert into #C values(3, 3)
insert into #A values(1, 1)
insert into #A values(2, 1)
insert into #A values(3, 2)
select
U.UserID,
U.Name,
U.StaffNo,
U.department,
coalesce(C.Point, 0)+coalesce(A.Point,0) as Points
from #U as U
left outer join
(select UserID, 1 as Point
from #C
-- where ...?
group by UserID) as C
on U.UserID = C.UserID
left outer join
(select UserID, 1 as Point
from #A
-- where ...?
group by UserID) as A
on U.UserID = A.UserID

Resources