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