how to make into an array and also count - oracle

I have this data:
app
asscs
mod_asscs
a
56
cb-56
a
67
cb-67
b
38
cb-38
a
12
12
I want to group by column 'app' and count the cases where 'mod_asscs' value is equal to concat('cb-', asscs). I also want to output the array in a separate column 'mod_asscs_array' so that the output is the following:
app
mod_asscs_array
scs_count
a
cb-56, cb-67
2
b
cb-38
1
So far this is what I have:
SELECT DISTINCT
app,
( CASE WHEN concat('cb-', asscs) = mod_asscs THEN mod_asscs || ',') AS mod_asscs_array,
COUNT( CASE WHEN concat('cb-', asscs) = mod_asscs THEN mod_asscs || ',') AS scs_count
FROM data_table
GROUP BY
app

Looks like aggregation.
Sample data:
SQL> with test (app, asscs, mod_asscs) as
2 (select 'a', 56, 'cb-56' from dual union all
3 select 'a', 67, 'cb-67' from dual union all
4 select 'b', 38, 'cb-38' from dual union all
5 select 'a', 12, '12' from dual
6 )
Query:
7 select app,
8 listagg(mod_asscs, ', ') within group (order by mod_asscs) array,
9 count(*) cnt
10 from test
11 where mod_asscs = 'cb-'|| asscs
12 group by app;
A ARRAY CNT
- -------------------- ----------
a cb-56, cb-67 2
b cb-38 1
SQL>

If you need to show all the "apps", even those with a "count" of zero, then you need conditional aggregation, something like this:
with
test (app, asscs, mod_asscs) as (
select 'a', 56, 'cb-56' from dual union all
select 'a', 67, 'cb-67' from dual union all
select 'b', 38, 'cb-38' from dual union all
select 'a', 12, '12' from dual union all
select 'c', 33, 'cb-23' from dual
)
select app,
listagg(case when mod_asscs = 'cb-' || asscs
then mod_asscs end, ', ')
within group (order by asscs) as mod_asscs_array,
count(case when mod_asscs = 'cb-' || asscs
then mod_asscs end) as scs_count
from test
group by app
order by app -- if needed
;
APP MOD_ASSCS_ARRAY SCS_COUNT
--- -------------------- ----------
a cb-56, cb-67 2
b cb-38 1
c 0

if there are duplicates in the array, do I do listagg(distinct.. and count(distinct case when ...?
You cannot use LISTAGG(DISTINCT ... as the LISTAGG function does not currently support the DISTINCT keyword; instead you need to use DISTINCT first in a sub-query and then use LISTAGG:
SELECT app,
LISTAGG(mod_asscs, ',') WITHIN GROUP (ORDER BY mod_asscs)
AS mod_asscs_array,
COUNT(*) AS scs_count
FROM (
SELECT DISTINCT
app,
mod_asscs
FROM data_table
WHERE 'cb-' || asscs = mod_asscs
)
GROUP BY app
Which, for the sample data:
CREATE TABLE data_table (app, asscs, mod_asscs) AS
SELECT 'a', 56, 'cb-56' FROM DUAL UNION ALL
SELECT 'a', 56, 'cb-56' FROM DUAL UNION ALL
SELECT 'a', 67, 'cb-67' FROM DUAL UNION ALL
SELECT 'b', 38, 'cb-38' FROM DUAL UNION ALL
SELECT 'a', 12, '12' FROM DUAL;
Outputs:
APP
MOD_ASSCS_ARRAY
SCS_COUNT
a
cb-56,cb-67
2
b
cb-38
1
db<>fiddle here

Related

IDENTITY full-time and part-time students

have a query and test CASE that shows the number of full-time and part-time students. A full-time student is enrolled in at least 4 courses. A part-time student is enrolled in at least 1 course, but no more than 3.
Although the query appears to work it seems a bit verbose. I was wondering if there is a more succinct way to rewrite the query. In addition, I can would like to display the students first/last names with each row that meets the criteria
Perhaps with something like this?
, LISTAGG(
NVL2(s.student_id, s.last_name || ', ' || s.first_name, NULL),
'; '
) WITHIN GROUP (ORDER BY s.last_name, s.first_name) AS students
Below are my tables, data and query I would like to shorten if possible. Thanks to all who answer and for your expertise.
CREATE TABLE students(student_id, first_name, last_name) AS
SELECT 1, 'Faith', 'Aaron' FROM dual UNION ALL
SELECT 2, 'Lisa', 'Saladino' FROM dual UNION ALL
SELECT 3, 'Leslee', 'Altman' FROM dual UNION ALL
SELECT 4, 'Patty', 'Kern' FROM dual UNION ALL
SELECT 5, 'Beth', 'Cooper' FROM dual UNION ALL
SELECT 95, 'Zak', 'Despart' FROM dual UNION ALL
SELECT 96, 'Owen', 'Balbert' FROM dual UNION ALL
SELECT 97, 'Jack', 'Aprile' FROM dual UNION ALL
SELECT 98, 'Nicole', 'Kramer' FROM dual UNION ALL
SELECT 99, 'Jill', 'Coralnick' FROM dual;
CREATE TABLE student_courses (student_id,course_id) AS
SELECT 1, 1 FROM dual UNION ALL
SELECT 2, 1 FROM dual UNION ALL
SELECT 3, 1 FROM dual UNION ALL
SELECT 4, 1 FROM dual UNION ALL
SELECT 5, 1 FROM dual UNION ALL
SELECT 1, 2 FROM dual UNION ALL
SELECT 2, 2 FROM dual UNION ALL
SELECT 3, 2 FROM dual UNION ALL
SELECT 4, 2 FROM dual UNION ALL
SELECT 5, 2 FROM dual UNION ALL
SELECT 1, 3 FROM dual UNION ALL
SELECT 2, 3 FROM dual UNION ALL
SELECT 3, 3 FROM dual UNION ALL
SELECT 4, 3 FROM dual UNION ALL
SELECT 5, 3 FROM dual UNION ALL
SELECT 97, 1 FROM dual UNION ALL
SELECT 97, 3 FROM dual UNION ALL
SELECT 97, 5 FROM dual UNION ALL
SELECT 97, 6 FROM dual UNION ALL
SELECT 98, 3 FROM dual UNION ALL
SELECT 98, 4 FROM dual UNION ALL
SELECT 98, 5 FROM dual UNION ALL
SELECT 99, 2 FROM dual UNION ALL
SELECT 99, 4 FROM dual UNION ALL
SELECT 99, 5 FROM dual UNION ALL
SELECT 99, 6 FROM dual;
WITH enrolled_student_course_counts AS (
SELECT
s.student_id
, s.first_name
, s.last_name
, COUNT(sc.course_id) AS course_count
FROM students s
LEFT JOIN student_courses sc
ON s.student_id = sc.student_id
GROUP BY
s.student_id
, s.first_name
, s.last_name
HAVING COUNT(sc.course_id) > 0
)
, student_enrollment_statuses AS (
SELECT
student_id
, first_name
, last_name
, CASE WHEN course_count >= 4 THEN 'full-time'
WHEN course_count BETWEEN 1 AND 3 THEN 'part-time'
END AS student_enrollment_status
FROM enrolled_student_course_counts
)
SELECT
UPPER(student_enrollment_status) AS student_enrollment_status
, COUNT(student_enrollment_status) AS student_enrollment_status_count
FROM student_enrollment_statuses
GROUP BY student_enrollment_status;
As you only need the numbers (and not any other data), shorten the query so that it searches only the student_courses table:
SQL> with temp as
2 (select student_id,
3 count(course_id) cnt
4 from student_courses
5 group by student_id
6 )
7 select
8 sum(case when cnt < 4 then 1 else 0 end) part_time,
9 sum(case when cnt >= 4 then 1 else 0 end) full_time
10 from temp;
PART_TIME FULL_TIME
---------- ----------
6 2
SQL>

Oracle is giving error while creating stored procedure. ORA-22905: cannot access rows from a non-nested table item

Hi all I have requirement to pick records using dynamic pivoting in Oracle. I have done with my query which is working fine using "antonsPivoting" https://technology.amis.nl/2006/05/24/dynamic-sql-pivoting-stealing-antons-thunder/.
When i add the query in procedure it gives error: ORA-22905: cannot access rows from a non-nested table item.
Sample table script:
CREATE TABLE DEPARTMENT(DEPT_ID NUMBER PRIMARY KEY, DEPT_NAME VARCHAR2(25))
CREATE TABLE EMPLOYEE(EMP_ID NUMBER, EMP_NAME VARCHAR(100), DEPT_ID NUMBER , FOREIGN KEY(DEPT_ID) REFERENCES DEPARTMENT(DEPT_ID))
INSERT INTO DEPARTMENT(DEPT_ID, DEPT_NAME)
SELECT 1, 'HR' FROM DUAL
UNION ALL SELECT 2, 'OPS' FROM DUAL
UNION ALL SELECT 3, 'MKT' FROM DUAL
UNION ALL SELECT 4, 'FIN' FROM DUAL
UNION ALL SELECT 5, 'IT' FROM DUAL
UNION ALL SELECT 6, 'SERV' FROM DUAL
COMMIT;
INSERT INTO EMPLOYEE(EMP_ID, EMP_NAME, DEPT_ID)
SELECT 1, 'A', 1 FROM DUAL
UNION ALL SELECT 1, 'A', 1 FROM DUAL
UNION ALL SELECT 2, 'B', 1 FROM DUAL
UNION ALL SELECT 3, 'C', 2 FROM DUAL
UNION ALL SELECT 4, 'D', 3 FROM DUAL
UNION ALL SELECT 5, 'E', 3 FROM DUAL
UNION ALL SELECT 6, 'F', 4 FROM DUAL
UNION ALL SELECT 7, 'G', 4 FROM DUAL
UNION ALL SELECT 8, 'H', 4 FROM DUAL
UNION ALL SELECT 9, 'I', 5 FROM DUAL
UNION ALL SELECT 10, 'J', 5 FROM DUAL
UNION ALL SELECT 11, 'K', 1 FROM DUAL
UNION ALL SELECT 12, 'L', 1 FROM DUAL
COMMIT;
sample query script working fine:
SELECT D.DEPT_NAME, COUNT(E.EMP_ID) TOTAL_EMP
FROM DEPARTMENT D
LEFT JOIN EMPLOYEE E ON D.DEPT_ID= E.DEPT_ID
GROUP BY D.DEPT_NAME
;
Dynamic pivoting example working fine:
select * from table( pivot( '
SELECT D.DEPT_NAME, COUNT(E.EMP_ID) TOTAL_EMP
FROM DEPARTMENT D
LEFT JOIN EMPLOYEE E ON D.DEPT_ID= E.DEPT_ID
GROUP BY D.DEPT_NAME' ) )
;
adding in procedure which gives error:
CREATE OR REPLACE PROCEDURE GET_EMPLOYEE (P_RESULT OUT SYS_REFCURSOR)
IS
BEGIN
OPEN P_RESULT FOR
select * from table( pivot( '
SELECT D.DEPT_NAME, COUNT(E.EMP_ID) TOTAL_EMP
FROM DEPARTMENT D
LEFT JOIN EMPLOYEE E ON D.DEPT_ID= E.DEPT_ID
GROUP BY D.DEPT_NAME' ) )
;
END
;
Have you created the Pivot function ?
Please go through below link where this is already answered.
Link

List all ancestors of a child in hierarchical data structure

Consider the following sample table:
with v1 as (
select 'I' as parent_id, 'M' as child_id from dual union all
select 'M', 'M1' from dual union all
select 'M', 'D' from dual union all
select 'I', 'P' from dual union all
select 'P', 'K' from dual union all
select 'A', 'P1' from dual union all
select 'C', 'A' from dual union all
select 'A', 'I' from dual union all
select 'P1', 'K1' from dual
)
select ListAgg(child_id,'<-')
within group(order by Level desc) as Path
from v1
START WITH child_id = 'D'
CONNECT BY PRIOR parent_id = child_id;
It returns:
A<-I<-M<-D
It is not returning A's parent which is C.
What should I change in the query so that it returns C as well like below:
C<-A<-I<-M<-D
NOTE that before executing the query, I have no means to know that C is the root of the hierarchy. So, I can not pass C in the query (and that is how I have designed the my query above).
You can use following query:
With v1 as (
select 'C' as parent_id, 'A' as child_id from dual union all
select 'I', 'M' from dual union all
select 'M', 'M1' from dual union all
select 'M', 'D' from dual union all
select 'I', 'P' from dual union all
select 'P', 'K' from dual union all
select 'A', 'P1' from dual union all
select 'A', 'I' from dual union all
select 'P1', 'K1' from dual
)
select ListAgg(case when level =1 then parent_id || '<-' || child_id else parent_id end,'<-')
within group(order by Level desc) as Path
from v1
START WITH child_id = 'D'
CONNECT BY prior parent_id = child_id;
Sqlfiddle demo
Cheers!!

Oracle Pivot Column in group query

I have the following working query:
WITH pivot_data AS (
select PSGROUP,
PSCOLUMN as PSCOLUMN
FROM LOG_PS_STATUS
)
SELECT *
FROM pivot_data
PIVOT (
MAX(NULL) --<-- pivot_clause
FOR PSCOLUMN--<-- pivot_for_clause
IN (&PS_COLUMNS.) --<-- pivot_in_clause
);
It shows results as expected:
Values:
PSGroup PSColumn
A 1
A 2
A 3
B 1
B 2
B 3
C 3
Result is giving like:
PSGroup(Column vertically) PSColoumn(Horizontally)
1 2 3
A
B
C
Now I want to make PSGroup column as group of PSColumn and output should be like:
A
1 2 3
B
1 2 3
C
3
You can use listagg:
WITH pivot_data(PSGroup,PSColumn) AS (
select 'A', 1 FROM dual UNION all
select 'A', 2 FROM dual UNION all
select 'A', 3 FROM dual UNION all
select 'B', 1 FROM dual UNION all
select 'B', 2 FROM dual UNION all
select 'B', 3 FROM dual UNION all
select 'C', 3 FROM DUAL),
res(PSGroup,PSColumns) as(
SELECT PSGroup, LISTAGG(PSColumn, ' ') WITHIN GROUP (order by PSColumn)
FROM pivot_data
GROUP BY PSGroup)
select DECODE(PSColumns,NULL,PSGroup,NULL) AS PSGroup, PSColumns from(
select PSGroup, NULL as PSColumns from res
union all
select PSGroup, PSColumns from res)t
ORDER BY t.PSGroup, t.PSColumns NULLS first
Also note that LISTAGG(PSColumn, ' ') WITHIN GROUP (order by PSColumn) is limited to 4000 characters

Oracle 9.2 pivot distinct value

The pivot function is available from Oracle 11 and i will need similar result using Oracle 9.2.
The main argument is that i need to pivot some values with a distinct result in a table like this:
id col3
1 a
1 b
--
2 a
2 a
2 b
--
3 a
3 b
3 c
My result sould be something like this
id a b c
1 1 1 0
2 1 1 0
3 1 1 1
To create a "manual" pivot i'm using a case/when but I am not able to understand how to get distinct value.
Right now the query is this:
with t as
( select 1 as id, 'a' as col1 from dual union all
select 1 as id, 'b' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'b' from dual union all
select 3 as id, 'a' from dual union all
select 3 as id, 'b' from dual union all
select 3 as id, 'c' from dual
)
select t.id,
count(case when t.col1 = 'a' then 1 end) a,
count(case when t.col1 = 'b' then 1 end) b,
count(case when t.col1 = 'c' then 1 end) c
This produce correct value but obviously it just "count" the total a/b/c value and not the distinct.
thanks for the support
If I correctly understand your need, you can try something like the following; it aggregates by id and counts the distinct values of col3:
with t as
( select 1 as id, 'a' as col1 from dual union all
select 1 as id, 'b' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'b' from dual union all
select 3 as id, 'a' from dual union all
select 3 as id, 'b' from dual union all
select 3 as id, 'c' from dual
)
select id,
count(distinct decode (col1, 'a', id, null)) a,
count(distinct decode (col1, 'b', id, null)) b,
count(distinct decode (col1, 'c', id, null)) c
from t
group by id
Of course the query depends on the number of different values of col3, but this is the same problem than pivot.

Resources