Group and exclude rows that had empty values aggregated - oracle sql - sql

I have a oracle sql table that looks like so
"STUDENT_ID","FULL_NAME","SEMESTER_ID","STIP_ID"
"1","Liam Bottrill","1","1"
"1","Liam Bottrill","2","3"
"1","Liam Bottrill","3","2"
"1","Liam Bottrill","4","5"
"2","Maurits Smitham","1","6"
"2","Maurits Smitham","2",""
"2","Maurits Smitham","3","2"
"2","Maurits Smitham","4","6"
"43","Jackie Cotton","1",""
"43","Jackie Cotton","2",""
"43","Jackie Cotton","3",""
"43","Jackie Cotton","4",""
I want to group this table by "STUDENT_ID" and exclude from result any students that have any of "STIP_ID" rows empty
Im aiming for result like this:
"STUDENT_ID","FULL_NAME"
"1","Liam Bottrill"
Liam Bottrill should be displayed while Maurits Smitham and Jackie Cotton should be excluded from result
Can you please help me with such aggregate function?

Here is one way, using aggregation:
SELECT *
FROM yourTable
WHERE STUDENT_ID IN (
SELECT STUDENT_ID
FROM yourTable
GROUP BY STUDENT_ID
HAVING COUNT(CASE WHEN STIP_ID IS NULL THEN 1 END) = 0
);
Another way, using exists logic:
SELECT t1.*
FROM yourTable t1
WHERE NOT EXISTS (
SELECT 1
FROM yourTable t2
WHERE t2.STUDENT_ID = t1.STUDENT_ID AND
t2.STIP_ID IS NULL
);

You can group by the identifier and then use conditional aggregation to find the student where the count when STIP_ID is NULL (which, in Oracle, is the same as an empty string):
SELECT student_id,
MAX(full_name) AS full_name
FROM table_name
GROUP BY student_id
HAVING COUNT(CASE WHEN stip_id IS NULL THEN 1 END) = 0;
Which, for your sample data:
CREATE TABLE table_name (STUDENT_ID, FULL_NAME, SEMESTER_ID, STIP_ID) AS
SELECT 1, 'Liam Bottrill', 1, 1 FROM DUAL UNION ALL
SELECT 1, 'Liam Bottrill', 2, 3 FROM DUAL UNION ALL
SELECT 1, 'Liam Bottrill', 3, 2 FROM DUAL UNION ALL
SELECT 1, 'Liam Bottrill', 4, 5 FROM DUAL UNION ALL
SELECT 2, 'Maurits Smitham', 1, 6 FROM DUAL UNION ALL
SELECT 2, 'Maurits Smitham', 2, NULL FROM DUAL UNION ALL
SELECT 2, 'Maurits Smitham', 3, 2 FROM DUAL UNION ALL
SELECT 2, 'Maurits Smitham', 4, 6 FROM DUAL UNION ALL
SELECT 43, 'Jackie Cotton', 1, NULL FROM DUAL UNION ALL
SELECT 43, 'Jackie Cotton', 2, NULL FROM DUAL UNION ALL
SELECT 43, 'Jackie Cotton', 3, NULL FROM DUAL UNION ALL
SELECT 43, 'Jackie Cotton', 4, NULL FROM DUAL;
Outputs:
STUDENT_ID
FULL_NAME
1
Liam Bottrill
db<>fiddle here

Related

looking to get a query constructed out for oracle sql developer output

Existing table
Output query should render this
Use GROUP BY and take the MAX of each column:
SELECT event,
MAX(numericval) AS quantity,
MAX(details) AS status
FROM table_name
GROUP BY event
Which, for the sample data:
CREATE TABLE table_name (event, elements, numericval, details) AS
SELECT 1, 'quality', 5, NULL FROM DUAL UNION ALL
SELECT 1, 'status', NULL, 'yes' FROM DUAL UNION ALL
SELECT 2, 'quality', 10, NULL FROM DUAL UNION ALL
SELECT 2, 'status', NULL, 'no' FROM DUAL UNION ALL
SELECT 3, 'quality', 15, NULL FROM DUAL UNION ALL
SELECT 3, 'status', NULL, 'yes' FROM DUAL;
Outputs:
EVENT
QUANTITY
STATUS
1
5
yes
2
10
no
3
15
yes
For your update, you can use conditional aggregation:
SELECT event,
MAX(CASE elements WHEN 'quality' THEN numericval END) AS quantity,
MAX(CASE elements WHEN 'status' THEN details END) AS status,
MAX(CASE elements WHEN 'Lot' THEN numericval END) AS lot
FROM table_name
GROUP BY event
Which for the updated data:
CREATE TABLE table_name (event, elements, numericval, details) AS
SELECT 1, 'quality', 5, NULL FROM DUAL UNION ALL
SELECT 1, 'status', NULL, 'yes' FROM DUAL UNION ALL
select 1 , 'Lot',1,null from dual union all
SELECT 2, 'quality', 10, NULL FROM DUAL UNION ALL
SELECT 2, 'status', NULL, 'no' FROM DUAL UNION ALL
select 2 , 'Lot',3,null from dual union all
SELECT 3, 'quality', 15, NULL FROM DUAL UNION ALL
SELECT 3, 'status', NULL, 'yes' FROM DUAL union all
select 3, 'Lot', 4,null from dual;
Outputs:
EVENT
QUANTITY
STATUS
LOT
1
5
yes
1
2
10
no
3
3
15
yes
4
db<>fiddle here

How to remove rows with different values within the same option value

In SQL, can someone help me understand what the query may look like if I'm trying to remove the red-highlighted rows from the image? Based on the logic I need, I need to remove records with different priority values within an option, where as the ones we want to keep have the same priority. Each item/cntry combination has it's own option values (typically 4 like you see here).
I feel like this is just a join to itself, but my mind is mush right now. Help would be appreciated!
You can use the analytic COUNT(DISTINCT ...) function and then you do not need to use a self-join:
SELECT *
FROM (
SELECT t.*,
COUNT(DISTINCT priority) OVER (PARTITION BY cntry, item, "OPTION")
AS cnt
FROM table_name t
)
WHERE cnt = 1;
Which, for the sample data:
CREATE TABLE table_name ("OPTION", priority, item, cntry) AS
SELECT 1, 1, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 1, 1, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 2, 2, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 2, 1, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 3, 1, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 3, 2, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 4, 2, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 4, 2, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 1, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 2, 2, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 2, 2, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 2, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 3, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 3, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 3, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 4, 2, 'HIJKLM', 'CAN' FROM DUAL;
Outputs:
OPTION
PRIORITY
ITEM
CNTRY
CNT
1
1
HIJKLM
CAN
1
3
1
HIJKLM
CAN
1
3
1
HIJKLM
CAN
1
3
1
HIJKLM
CAN
1
4
2
HIJKLM
CAN
1
1
1
ABCDEF
USA
1
1
1
ABCDEF
USA
1
4
2
ABCDEF
USA
1
4
2
ABCDEF
USA
1
db<>fiddle here
use exists and distinct count()
select t1.* from table_name t1 where
exists (
select 1
from table_name t2
where t1.option=t2.option
group by option
having count (distinct priority)=1
)

converting comma separated value to multiple rows

I have a table like this:
ID NAME Dept_ID
1 a 2,3
2 b
3 c 1,2
Department is another table having dept_id and dept_name as columns. i want result like,
ID Name Dept_ID
1 a 2
1 a 3
2 b
3 c 1
3 c 2
any help please?
You can do it as:
--Dataset Preparation
with tab(ID, NAME,Dept_ID) as (Select 1, 'a', '2,3' from dual
UNION ALL
Select 2, 'b','' from dual
UNION ALL
Select 3, 'c' , '1,2' from dual)
--Actual Query
select distinct ID, NAME, regexp_substr(DEPT_ID,'[^,]+', 1, level)
from tab
connect by regexp_substr(DEPT_ID,'[^,]+', 1, level) is not null
order by 1;
Edit:
based on which column i need to join? in one table i have comma
separated ids and in other table i have just ids
with tab(ID, NAME,Dept_ID) as (Select 1, 'a', '2,3' from dual
UNION ALL
Select 2, 'b','' from dual
UNION ALL
Select 3, 'c' , '1,2' from dual) ,
--Table Dept
tbl_dept (dep_id,depname) as ( Select 1,'depa' from dual
UNION ALL
Select 2,'depb' from dual
UNION ALL
Select 3,'depc' from dual
) ,
--Seperating col values for join. Start your query from here using with clause since you already have the two tables.
tab_1 as (select distinct ID, NAME, regexp_substr(DEPT_ID,'[^,]+', 1, level) col3
from tab
connect by regexp_substr(DEPT_ID,'[^,]+', 1, level) is not null
order by 1)
--Joining table.
Select t.id,t.name,t.col3,dt.depname
from tab_1 t
left outer join tbl_dept dt
on t.col3 = dt.dep_id
order by 1
with tmp_tbl as(
select
1 ID,
'a' NAME,
'2,3' DEPT_ID
from dual
union all
select
2 ID,
'b' NAME,
'' DEPT_ID
from dual
union all
select
3 ID,
'c' NAME,
'1,2' DEPT_ID
from dual)
select
tmp_out.ID,
tmp_out.NAME,
trim(tmp_out.DEPT_ID_splited)
from(
select
tmp.ID,
tmp.NAME,
regexp_substr(tmp.DEPT_ID,'[^,]+', 1, level) DEPT_ID_splited
from
tmp_tbl tmp
connect by
regexp_substr(tmp.DEPT_ID,'[^,]+', 1, level) is not null) tmp_out
group by
tmp_out.ID,
tmp_out.NAME,
tmp_out.DEPT_ID_splited
order by
tmp_out.ID,
tmp_out.DEPT_ID_splited

How to upside down the result rows of a select statment in oracle?

I can select a list of rows from a table. but I want to show them by swapping upside down.
Explaination:
with table1 as
(
select 1 ID, 'txt1' value from dual
union all
select 2, 'txt2' from dual
union all
select 7, 'txt7' from dual
union all
select 5, 'txt5' from dual
union all
select 3, 'txt3' from dual
)
select * from table1;
in above query I can obtain following result
ID | VALUE
------------------
1 txt1
2 txt2
7 txt7
5 txt5
3 txt3
but I want to show them as follows
ID | VALUE
------------------
3 txt3
5 txt5
7 txt7
2 txt2
1 txt1
How to do that?
One approach would be to add a computed column to your set of union queries, then order by that column:
WITH table1 AS (
SELECT 1 ID, 'txt1' value, 1 AS position FROM dual
UNION ALL
SELECT 2, 'txt2', 2 FROM dual
UNION ALL
SELECT 7, 'txt7', 3 FROM dual
UNION ALL
SELECT 5, 'txt5', 4 FROM dual
UNION ALL
SELECT 3, 'txt3', 5 FROM dual
)
SELECT *
FROM table1
ORDER BY pos DESC;
Note that there is no internal order to a SQL table in general. Actually, even the current ordering you are observing is not necessarily guaranteed by Oracle. If you expect a certain order in a result set, you need to impose it via a ORDER BY clause.
How's this?
with table1 as
(
select 1 ID, 'txt1' value from dual
union all
select 2, 'txt2' from dual
union all
select 7, 'txt7' from dual
union all
select 5, 'txt5' from dual
union all
select 3, 'txt3' from dual
)
select * from table1 order by rownum desc;
Actually this is not working for this perticular example. but it is working for normal table.

How to select the minimum value in a table or the next one in oracle sql

I have a table L1_CI_PER_ADDRESS with these columns
PER_ID,
SEQ_NUM,
ADDRESS_ID,
ADDRESS_TYPE_XFLG,
START_DT,
END_DT,
SEASON_START_MMDD,
SEASON_END_MMDD,
ADDRESS_PRIO_FLG,
DELIVERABLE_FLG,
VERSION,
LOAD_DATE
I want to select ADDRESS_TYPE_XFLG where the value is MAIN-AE if it exists or the MAIN-EN if the first one does not exists. Else I want to select CORRESPOND-AE or CORRESPOND-AE if MAIN-AE and MAIN-EN do not exists.
How can I do this? I am new to Oracle SQL. I want to remove the duplicates returned when I do my select.
One of the issues is that some person ID's have all four (MAIN-AE, MAIN-EN, CORRESPOND-AE, CORRESPOND-EN), so in this case I just want MAIN-AE to be returned.
I hope my question is clear.
enter image description here
It's top-n query. Use row_number():
select *
from (
select PER_ID, address_id, ADDRESS_TYPE_XFLG,
row_number() over (partition by per_id
order by case ADDRESS_TYPE_XFLG
when 'MAIN-AE' then 1
when 'MAIN-EN' then 2
when 'CORRESPOND-AE' then 3
when 'CORRESPOND-EN' then 4
end) as rn
from L1_CI_PER_ADDRESS)
where rn = 1
If person can own two addresses with the same flag then you need to add proper order after case when section, probably something like , seq_num desc.
Test:
with L1_CI_PER_ADDRESS(PER_ID, address_id, ADDRESS_TYPE_XFLG ) as (
select 1, 1, 'CORRESPOND-AE' from dual union all
select 1, 2, 'MAIN-AE' from dual union all
select 1, 3, 'CORRESPOND-EN' from dual union all
select 1, 4, 'MAIN-EN' from dual union all
select 2, 5, 'CORRESPOND-AE' from dual union all
select 3, 6, 'MAIN-AE' from dual union all
select 4, 7, 'CORRESPOND-EN' from dual union all
select 4, 8, 'MAIN-AE' from dual
)
select PER_ID, address_id
from (
select PER_ID, address_id, ADDRESS_TYPE_XFLG,
row_number() over (partition by per_id
order by case ADDRESS_TYPE_XFLG
when 'MAIN-AE' then 1
when 'MAIN-EN' then 2
when 'CORRESPOND-AE' then 3
when 'CORRESPOND-EN' then 4
end) as rn
from L1_CI_PER_ADDRESS)
where rn = 1
Output:
PER_ID ADDRESS_ID ADDRESS_TYPE_XFLG
---------- ---------- -----------------
1 2 MAIN-AE
2 5 CORRESPOND-AE
3 6 MAIN-AE
4 8 MAIN-AE