Retrieving and displaying data from oracle database - oracle

First of all, I would like to THANK YOU for stopping by and spending your precious time for looking at my problem.
I have 2 different tables within an Oracle Database.
The first table holds metadata about the columns present in the other table. Think of the first (COL_TAB) table as a custom version of the ALL_TAB_COLS which comes by default with Oracle.
COL_TAB
----------------------------------------------
| TABLE_NAME | COL_NAME | COL_DESC |
----------------------------------------------
| TABLE1 | TAB1_COL_2 | TABLE 1 COLUMN 2 |
| TABLE1 | TAB1_COL_4 | TABLE 1 COLUMN 4 |
| TABLE1 | TAB1_COL_3 | TABLE 1 COLUMN 3 |
| TABLE1 | TAB1_COL_5 | |
| TABLE1 | TAB1_COL_1 | TABLE 1 COLUMN 1 |
----------------------------------------------
TABLE1
--------------------------------------------------------------------
| TAB1_COL_3 | TAB1_COL_1 | TAB1_COL_5 | TAB1_COL_2 |
--------------------------------------------------------------------
| TAB1_COL3_DATA1 | TAB1_COL1_DAT | TAB1_COL5_DAT2 | TAB1_COL2_DAT |
| TAB1_COL3_DATA2 | TAB1_COL1_DAT | TAB1_COL5_DAT1 | TAB1_COL2_DAT |
| TAB1_COL3_DATA3 | TAB1_COL1_DAT | TAB1_COL5_DAT3 | TAB1_COL2_DAT |
--------------------------------------------------------------------
I want to display the data as 2 different outputs:
FIRST OUTPUT:
------------------------------------------------------------------------------------------------
| TABLE 1 COLUMN 3 | TABLE 1 COLUMN 1 | TAB1_COL_5 | TABLE 1 COLUMN 2 | TABLE 1 COLUMN 4 |
------------------------------------------------------------------------------------------------
-> In case, if the COL_DESC is blank or null, then the COL_NAME needs to be displayed in the output.
-> "TABLE 1 COLUMN 3" AND "TABLE 1 COLUMN 1" always need to be displayed as 1st and 2nd column followed by the rest of the columns.
-> In case, if any column defined within the COL_TAB table isn't being used in TABLE1, then such a column needs to be displayed at the last column in the output,
for example, the TAB1_COL_4 isn't being used in TABLE1, so it is being displayed in the last.
SECOND OUTPUT:
------------------------------------------------------------------------------------------------
| TAB1_COL3_DATA1 | TAB1_COL1_DAT | TAB1_COL5_DAT2 | TAB1_COL2_DAT | |
| TAB1_COL3_DATA2 | TAB1_COL1_DAT | TAB1_COL5_DAT1 | TAB1_COL2_DAT | |
| TAB1_COL3_DATA3 | TAB1_COL1_DAT | TAB1_COL5_DAT3 | TAB1_COL2_DAT | |
------------------------------------------------------------------------------------------------
-> The order of the COLUMNS in the SECOND OUTPUT needs to be in sync with the order of columns as displayed within in the FIRST OUTPUT.
I did try the below query for displaying the FIRST OUTPUT, but it isn't working (I'm sure it's not correct):
SELECT NVL(COL_DESC, COL_NAME) AS COL_TEXT
FROM COL_TAB
WHERE TABLE_NAME = 'TABLE1'
PIVOT(MIN(COL_TEXT)
FOR COL_TEXT IN (SELECT COL_NAME FROM COL_TAB WHERE TABLE_NAME = 'TABLE1'));
In case, if anything isn't clear please do let me know. I would try my best to explain it again. Thanks again for your help in advance.

You can get the column description/names - in a deterministic order - with something like:
select coalesce(ct.col_desc, ct.col_name)
from col_tab ct
left join user_tab_columns utc
on utc.table_name = ct.table_name and utc.column_name = ct.col_name
where ct.table_name = 'TABLE1'
order by utc.column_id, ct.col_name;
COALESCE(CT.COL_
----------------
TABLE 1 COLUMN 3
TABLE 1 COLUMN 1
TAB1_COL_5
TABLE 1 COLUMN 2
TABLE 1 COLUMN 4
Pivoting those rows to columns would need to be done dynamically.
You can also generate a dynamic query to get the data in the same order in a similar way.
This uses SQL*Plus (or SQLcl, or SQL Developer) bind variable ref cursors to get the two outputs, and uses a table name defined within the block; but could easily be adapted to be a procedure that is passed the table name and have out parameters for the ref cursors:
var rc1 refcursor;
var rc2 refcursor;
declare
l_table_name varchar2(30) := 'TABLE1';
l_stmt varchar2(4000);
begin
select 'select '
|| listagg('''' || coalesce(ct.col_desc, ct.col_name) || '''', ',')
within group (order by utc.column_id, ct.col_name)
|| ' from dual'
into l_stmt
from col_tab ct
left join user_tab_columns utc
on utc.table_name = ct.table_name and utc.column_name = ct.col_name
where ct.table_name = l_table_name;
dbms_output.put_line(l_stmt);
open :rc1 for l_stmt;
select 'select '
|| listagg(coalesce(utc.column_name, 'null') || ' as ' || ct.col_name, ',')
within group (order by utc.column_id, ct.col_name)
|| ' from ' || l_table_name
into l_stmt
from col_tab ct
left join user_tab_columns utc
on utc.table_name = ct.table_name and utc.column_name = ct.col_name
where ct.table_name = l_table_name;
dbms_output.put_line(l_stmt);
open :rc2 for l_stmt;
end;
/
Running the block gets dbms_output of the statements just for debugging, but might be of interest:
select 'TABLE 1 COLUMN 3','TABLE 1 COLUMN 1','TAB1_COL_5','TABLE 1 COLUMN 2','TABLE 1 COLUMN 4' from dual
select TAB1_COL_3 as TAB1_COL_3,TAB1_COL_1 as TAB1_COL_1,TAB1_COL_5 as TAB1_COL_5,TAB1_COL_2 as TAB1_COL_2,null as TAB1_COL_4 from TABLE1
and then you can print the ref cursors (again, client-specific behaviour):
print rc1
'TABLE1COLUMN3' 'TABLE1COLUMN1' 'TAB1_COL_ 'TABLE1COLUMN2' 'TABLE1COLUMN4'
---------------- ---------------- ---------- ---------------- ----------------
TABLE 1 COLUMN 3 TABLE 1 COLUMN 1 TAB1_COL_5 TABLE 1 COLUMN 2 TABLE 1 COLUMN 4
print rc2
TAB1_COL_3 TAB1_COL_1 TAB1_COL_5 TAB1_COL_2 TAB1_COL_4
--------------- ------------- -------------- ------------- ----------
TAB1_COL3_DATA1 TAB1_COL1_DAT TAB1_COL5_DAT2 TAB1_COL2_DAT
TAB1_COL3_DATA2 TAB1_COL1_DAT TAB1_COL5_DAT1 TAB1_COL2_DAT
TAB1_COL3_DATA3 TAB1_COL1_DAT TAB1_COL5_DAT3 TAB1_COL2_DAT
Those 2 columns are common across all the tables.
In that case you can use a case expression to extend the ordering logic:
within group (order by case ct.col_name
when 'TAB1_COL_3' then 1
when 'TAB1_COL_1' then 2
else 3 end,
utc.column_id, ct.col_name)
which then gets:
'TABLE1COLUMN3' 'TABLE1COLUMN1' 'TAB1_COL_ 'TABLE1COLUMN2' 'TABLE1COLUMN4'
---------------- ---------------- ---------- ---------------- ----------------
TABLE 1 COLUMN 3 TABLE 1 COLUMN 1 TAB1_COL_5 TABLE 1 COLUMN 2 TABLE 1 COLUMN 4
TAB1_COL_3 TAB1_COL_1 TAB1_COL_5 TAB1_COL_2 TAB1_COL_4
--------------- ------------- -------------- ------------- ----------
TAB1_COL3_DATA1 TAB1_COL1_DAT TAB1_COL5_DAT2 TAB1_COL2_DAT
TAB1_COL3_DATA2 TAB1_COL1_DAT TAB1_COL5_DAT1 TAB1_COL2_DAT
TAB1_COL3_DATA3 TAB1_COL1_DAT TAB1_COL5_DAT3 TAB1_COL2_DAT
or possibly using the description instead of the name, depending on whether it's the name or description that stays the same (hard to guess from the example).
It would be really great if you could show up how pivoting can be done dynamically.
it isn't really needed here in the end, and is more complicated than the listagg I used above; but you could do something like;
select '
select * from (
select row_number()
over (order by case ct.col_name
when ''TAB1_COL_3'' then 1
when ''TAB1_COL_1'' then 2
else 3
end,
utc.column_id, ct.col_name) as pos,
coalesce(ct.col_desc, ct.col_name) as name
from col_tab ct
left join user_tab_columns utc
on utc.table_name = ct.table_name and utc.column_name = ct.col_name
where ct.table_name = :tab
)
pivot (max(name) as col for (pos) in ('
|| listagg(level, ',') within group (order by level)
|| '))'
into l_stmt
from dual
connect by level <= (select count(*) from col_tab where table_name = l_table_name);
dbms_output.put_line(l_stmt);
open :rc1 for l_stmt using l_table_name;
which gets output showing the generated dynamic query as:
select * from (
select row_number()
over (order by case ct.col_name
when 'TAB1_COL_3' then 1
when 'TAB1_COL_1' then 2
else 3
end,
utc.column_id, ct.col_name) as pos,
coalesce(ct.col_desc, ct.col_name) as name
from col_tab ct
left join user_tab_columns utc
on utc.table_name = ct.table_name and utc.column_name = ct.col_name
where ct.table_name = :tab
)
pivot (max(name) as col for (pos) in (1,2,3,4,5))
and result set as:
1_COL 2_COL 3_COL 4_COL 5_COL
---------------- ---------------- ---------------- ---------------- ----------------
TABLE 1 COLUMN 3 TABLE 1 COLUMN 1 TAB1_COL_5 TABLE 1 COLUMN 2 TABLE 1 COLUMN 4
You could use the column names for the pivot instead of the pos, it would just make it even harder to read I think, as you'd need to include quotes around them.

Related

Pull conditional statement results based on multiple table joins

I have 3 tables to join to get the output in the below format.
My table 1 is like:
--------------------------------------------------------
T1_ID1 | T1_ID2 | NAME
--------------------------------------------------------
123 | T11231 | TestName11
123 | T11232 | TestName12
234 | T1234 | TestName13
345 | T1345 | TestName14
--------------------------------------------------------
My table 2 is like:
--------------------------------------------------------
T2_ID1 | T2_ID2 | NAME
--------------------------------------------------------
T11231 | T21231 | TestName21
T11232 | T21232 | TestName21
T1234 | T2234 | TestName22
--------------------------------------------------------
My table 3 is like:
----------------------------------------------------------
T3_ID1 | TYPE | REF
----------------------------------------------------------
T21231 | 1 | 123456
T21232 | 2 | 1234#test.com
T2234 | 2 | 123#test.com
----------------------------------------------------------
My desired output is:
------------------------------------------------------
T1_ID1 | PHONE | EMAIL
------------------------------------------------------
123 | 123456 | 1234#test.com
234 | | 123#test.com
345 | |
------------------------------------------------------
Requirements:
T1_ID2 of table 1 left joins with T2_ID1 of table 2.
T2_ID2 of table 2 left joins with T3_ID1 of table 3.
TYPE of table 3 specifies 1 if the value is phone and specified 2 if value is email.
My output should contain T1_ID1 of table 1 and its corresponding value of REF in table 3, with the REF in the same row.
That is, in this case, T1_ID1 with value 123 has both phone and email. So, it is displayed in the same row in output.
If phone alone is available for corresponding value of T1_ID1, then phone should be populated in the result with email as null and vice versa.
If neither phone nor email is available, nothing should be populated.
I had tried the below SQLs but in vain. Where am I missing? Please extend your help.
Option 1:
SELECT DISTINCT
t1.t1_id1,
t3.ref
|| (
CASE
WHEN t3.type = 1 THEN
1
ELSE
0
END
) phone,
t3.ref
|| (
CASE
WHEN t3.type = 2 THEN
1
ELSE
0
END
) email
FROM
table1 t1
LEFT JOIN table2 t2 ON t1.t1_id2 = t2.t2_id1
LEFT JOIN table3 t3 ON t2.t2_id2 = t3.t3_id1;
Option 2:
SELECT DISTINCT
t1.t1_id1,
t3.ref,
(
CASE
WHEN t3.type = 1 THEN
1
ELSE
0
END
) phone,
t3.ref,
(
CASE
WHEN t3.type = 2 THEN
1
ELSE
0
END
) email
FROM
table1 t1
LEFT JOIN table2 t2 ON t1.t1_id2 = t2.t2_id1
LEFT JOIN table3 t3 ON t2.t2_id2 = t3.t3_id1;
Option 3:
SELECT DISTINCT
t1.t1_id1,
(
CASE
WHEN t3.type = 1 THEN
1
ELSE
0
END
) phone,
(
CASE
WHEN t3.type = 2 THEN
1
ELSE
0
END
) email
FROM
table1 t1
LEFT JOIN table2 t2 ON t1.t1_id2 = t2.t2_id1
LEFT JOIN table3 t3 ON t2.t2_id2 = t3.t3_id1;
select t1_id1, max(t3.ref )phone, max(t33.ref) email
from table1
left outer join
table2 on t1_id2=t2_id1
left outer join table3 t3 on t3.t3_id1=t2_id2 and t3.type=1
left outer join table3 t33 on t33.t3_id1=t2_id2 and t33.type=2
group by t1_id1
if you have maximum one phone and one email in table3 for each t2_id2 entry in table2.

Generate duplicate rows with incremental values in Oracle

I have a requirement in Oracle SQL that specific values should be repeated 5 times. So, I have written the below query and getting the result as expected.
SELECT Item_Name || RANGES AS Item_Number
FROM
(
SELECT DISTINCT 'Price Line ' || column1 || '-' AS Item_Name, level+0 RANGES
FROM
(
SELECT DISTINCT column1 from tbl where column1 in
(
'ABC',
'BCD'
)
) connect by level <= 5
) order by Item_Number
OUTPUT:
Price Line ABC-1
Price Line ABC-2
Price Line ABC-3
Price Line ABC-4
Price Line ABC-5
Price Line BCD-1
Price Line BCD-2
Price Line BCD-3
Price Line BCD-4
Price Line BCD-5
But when I add more than 10 values like 'DEF', 'EFG',.....'XYZ', the query keeps executing for hours without any result.
Any help or suggestion on this would be appreciated.
Regards,
Make sure the CONNECT BY is not operating on your table rows. For example, use a common table expression (i.e., a WITH clause) to create a row source with 5 rows using CONNECT BY and then CROSS JOIN that row source to your table. Here is an example.
CREATE TABLE ITEMS ( item_number VARCHAR2(30) );
INSERT INTO ITEMS VALUES ('ABC');
INSERT INTO ITEMS VALUES ('BCD');
INSERT INTO ITEMS VALUES ('CDE');
INSERT INTO ITEMS VALUES ('DEF');
INSERT INTO ITEMS VALUES ('EFG');
INSERT INTO ITEMS VALUES ('FGH');
INSERT INTO ITEMS VALUES ('GHI');
INSERT INTO ITEMS VALUES ('HIJ');
INSERT INTO ITEMS VALUES ('IJK');
INSERT INTO ITEMS VALUES ('JKL');
COMMIT;
WITH rowgen AS (
SELECT rownum rn FROM dual CONNECT BY rownum <= 5 )
SELECT item_number || '-' || rn
FROM items
CROSS JOIN rowgen
ORDER BY item_number, rn;
+----------------------+
| ITEM_NUMBER||'-'||RN |
+----------------------+
| ABC-1 |
| ABC-2 |
| ABC-3 |
| ABC-4 |
| ABC-5 |
| BCD-1 |
| BCD-2 |
| BCD-3 |
| BCD-4 |
| BCD-5 |
| CDE-1 |
| CDE-2 |
| CDE-3 |
| CDE-4 |
| CDE-5 |
| ... |
+----------------------+

IN statement from CASE result inside Where clause Oracle

I have a sample of the table and problem I am trying to solve in Oracle.
CREATE TABLE mytable (
id_field number
,status_code number
,desc1 varchar2(15)
);
INSERT INTO mytable VALUES (1,240,'desc1');
INSERT INTO mytable VALUES (2,242,'desc1');
INSERT INTO mytable VALUES (3,241,'desc1');
INSERT INTO mytable VALUES (4,244,'desc1');
INSERT INTO mytable VALUES (5,240,'desc2');
INSERT INTO mytable VALUES (6,242,'desc2');
INSERT INTO mytable VALUES (7,245,'desc2');
INSERT INTO mytable VALUES (8,246,'desc2');
INSERT INTO mytable VALUES (9,246,'desc1');
INSERT INTO mytable VALUES (10,242,'desc1');
commit;
SELECT *
FROM mytable
WHERE status_code IN CASE WHEN desc1 = 'desc1' THEN (240,242)
WHEN desc1 = 'desc2' THEN (240,245)
END
Basically I need to select a subset of status codes for each condition.
I could solve this with separate statements but the actual table I am doing this on has multiple descriptions and would result in around 20 unioned queries.
Any way to do this in one statement like I have attempted?
I believe that a CASE statement can only return one value (corresponding to one column in the result set). However, you can achive this in your WHERE clause without using a CASE statement:
WHERE (desc1 = 'desc1' AND status_code IN (240,242)) OR
(desc1 = 'desc2' AND status_code IN (240,245))
I like Tim answer better, but at least in postgres you can do this. Couldnt try it on oracle
Sql Fiddle DEMO
SELECT *
FROM mytable
WHERE CASE WHEN desc1 = 'desc1' THEN status_code IN (240,242)
WHEN desc1 = 'desc2' THEN status_code IN (240,245)
END
ORDER BY desc1
OUTPUT
| id_field | status_code | desc1 |
|----------|-------------|-------|
| 1 | 240 | desc1 |
| 2 | 242 | desc1 |
| 10 | 242 | desc1 |
| 5 | 240 | desc2 |
| 7 | 245 | desc2 |

How to conditionally join a table function in Oracle SQL, which has a primary table row column as an argument, without excessive function calls?

I have the following issue:
Every row in my base table have a flag column ('flg' in the listing below) and an function argument column ('arg'). If in a row #N the flag is 'Y', then function has to be called; after, it shall return, let's say, a column (actually a bunch of them, but i'll simplify as much as i can). And finally, the row #N should transform into a sub-table - full join of a row #N and the column, returned by the function.
If the flag is 'N' then a result for the row #N shall be as the row itself plus NULL in that "function return column".
So here's an example:
create or replace package SIEBEL.TEST_PACKAGE as
type ret_type is table of number;
function testFunc(inp number)
return ret_type
pipelined;
end TEST_PACKAGE;
/
create or replace package body SIEBEL.TEST_PACKAGE is
function testFunc(inp number)
return ret_type
pipelined
is
i number;
begin
dbms_output.put_line('Function call, arg = ' || to_char(inp));
if (inp is null OR inp = 0) then
pipe row (null);
else
for i in 1..inp loop
pipe row (i);
end loop;
end if;
end testFunc;
end TEST_PACKAGE;
/
with base_table as
(
select 'Shall invoke' col0, 'Y' flg, '2' arg from dual
union
select 'Shall not invoke' col0, 'N' flg, '0' arg from dual
)
select * from base_table t0, table(siebel.test_package.testFunc(t0.arg)) t1;
It will return what i actually want:
COL0 | FLG | ARG | COLUMN_VALUE
--------------------------------------------
Shall invoke | Y | 2 | 1
Shall invoke | Y | 2 | 2
Shall not invoke | N | 0 |
The thing is, even for the 'N' flag the function is still called - the database output will show
Function call, arg = 2
Function call, arg = 0
If a real world i have hundreds of records with 'Y' flag, ~100K with 'N'. Alse my actual function is much more complicated and have a lot of stuff in its namespace, so every function call is crucial in terms of perfomance.
What i do want is the database output for the example:
Function call, arg = 2
I could achieve it with
with base_table as
(
select 'Shall invoke' col0, 'Y' flg, '2' arg from dual
union
select 'Shall not invoke' col0, 'N' flg, '0' arg from dual
)
select t.*, t1.column_value
from base_table t, table(SIEBEL.TEST_PACKAGE.testFunc(t.arg)) t1
where t.flg = 'Y'
union all
select t.*, null as column_value
from base_table t
where t.flg = 'N';
but then all indexes became useless - every 'order by' instruction will take alot to complete.
Please, help me to achieve the desired behaviour of function calls and still to save the primal rows order.
Feel free to ask me if anything is not clear.
Best Regards,
Alexey
For flag = "N" the function is not called if you conditionally join
set serveroutput on
with base_table as
(
select 'Shall invoke' col0, 'Y' flg, '2' arg from dual
union
select 'Shall not invoke' col0, 'N' flg, '0' arg from dual
)
select * from base_table t0
left join table( test_package.testFunc(t0.arg) ) t1 on (t0.flg = 'Y');
Script Output
Package created.
Package body created.
COL0 FLG ARG COLUMN_VALUE
---------------- --- --- ------------
Shall invoke Y 2 1
Shall invoke Y 2 2
Shall not invoke N 0
3 rows selected.
Server Output:
Function call, arg = 2
To avoid the function call for flag = 'N', filter out the row.
Add where flg='Y' in the filter predicate.
For better performance, create an index on flg column.
SQL> set serveroutput on
SQL> with base_table as
2 (
3 select 'Shall invoke' col0, 'Y' flg, '2' arg from dual
4 union
5 select 'Shall not invoke' col0, 'N' flg, '0' arg from dual
6 )
7 SELECT * FROM base_table t0, TABLE(test_package.testFunc(t0.arg)) t1
8 where flg='Y';
COL0 F A COLUMN_VALUE
---------------- - - ------------
Shall invoke Y 2 1
Shall invoke Y 2 2
Function call, arg = 2
SQL>
Edit
To have the rows for 'flg='N' also in the output, you could do an OUTER JOIN, no need of UNION.
For example,
SQL> CREATE table base_table AS
2 select * from(
3 ( SELECT 'Shall invoke' col0, 'Y' flg, '2' arg FROM dual
4 UNION
5 SELECT 'Shall not invoke' col0, 'N' flg, '0' arg FROM dual
6 )
7 );
Table created.
SQL>
SQL> SELECT *
2 FROM base_table t0
3 LEFT JOIN TABLE( test_package.testFunc(t0.arg) ) t1
4 ON (t0.flg = 'Y');
COL0 F A COLUMN_VALUE
---------------- - - ------------
Shall invoke Y 2 1
Shall invoke Y 2 2
Shall not invoke N 0
SQL>
You can check the explain plan to see how the function is called and how many times is it executed.
SQL> EXPLAIN PLAN FOR
2 SELECT *
3 FROM base_table t0
4 LEFT JOIN TABLE( test_package.testFunc(t0.arg) ) t1
5 ON (t0.flg = 'Y');
Explained.
SQL>
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------
Plan hash value: 2726614787
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16336 | 510K| 61 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 16336 | 510K| 61 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | BASE_TABLE | 2 | 38 | 3 (0)| 00:00:01 |
| 3 | VIEW | VW_LAT_D4FD8C38 | 8168 | 103K| 29 (0)| 00:00:01 |
|* 4 | FILTER | | | | | |
| 5 | COLLECTION ITERATOR PICKLER FETCH| TESTFUNC | 8168 | 16336 | 29 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("T0"."FLG"='Y')
17 rows selected.
SQL>

Using DISTINCT for specific columns

select distinct employee_id, first_name, commission_pct, department_id from
employees;
When I use the above query it results in distinct combination of all the attributes mentioned. As employee_id (being the primary key for employees) is unique, the query results in producing all the rows in the table.
I want to have a result set that has distinct combination of commission_pct and department_id. so how the query should be formed. When I tried to include the DISTINCT in the middle as
select employee_id, first_name, distinct commission_pct, department_id from
employees;
It is resulting in an error
ORA-00936-missing expression
How to form a query which results have only distinct combination of commission and department_id.The table is from HR schema of oracle.
What you request is impossible. You cannot select all the employee ids but have only distinct commission_pct and department_id.
So think it over, what you want to show:
All distinct commission_pct, department_id only?
All distinct commission_pct, department_id and the number of relevant employees?
All distinct commission_pct, department_id and the relevant employees comma separated?
All employees, but with nulls when commission_pct and department_id are the same as in the line before?
The first can be solved with DISTINCT. The second and third with GROUP BY (plus count or listagg). The last would be solved with the analytic function LAG.
You have to remove two columns before distinct
select distinct commission_pct, department_id from
employees;
Indeed, if your second query would work, what do you expect to see in the first two columns? Consider example data
| employee_id | first_name | commission_pct | department_id |
| 1 | "x" | "b" | 3 |
| 2 | "y" | "b" | 3 |
| 1 | "x" | "c" | 4 |
| 2 | "y" | "c" | 4 |
You expect to get only two row result like this
| employee_id | first_name | commission_pct | department_id |
| ? | ? | "b" | 3 |
| ? | ? | "c" | 4 |
But what do you expect in the first two column?
Can you try this one?
SELECT
NAME1,
PH
FROM
(WITH T
AS (SELECT
'mark' NAME1,
'1234567' PH
FROM
DUAL
UNION ALL
SELECT
'bailey',
'456789'
FROM
DUAL
UNION ALL
SELECT
'mark',
'987654'
FROM
DUAL)
SELECT
NAME1,
PH,
ROW_NUMBER ( ) OVER (PARTITION BY NAME1 ORDER BY NAME1) SEQ
FROM
T)
WHERE
SEQ = 1;
If you dont care on a specific row, then use aggregate functions
SELECT
NAME1,
MAX ( PH ) PH
FROM
T
GROUP BY
NAME1;

Resources