Question

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:

enter image description here

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

Was it helpful?

Solution

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             
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top