Oracle PIVOT - unused columns affect rollup - oracle

I've got a dataset that consists of several key fields, a status, and a count. PIVOT does a nice job of giving me one row per key field combo, with one column per status. Sample below:
with dataquery (field1, field2, field3, my_status, statcount) as (
select 'AAA', 'BBB', 'CCC', 'XX', 11 from dual
union
select 'AAA', 'BBB', 'CCC', 'YY', 22 from dual
union
select 'AAA', 'BBB', 'CCC', 'ZZ', 33 from dual
),
pivotquery as (
SELECT field1,
field2,
field3,
xx_stat_count AS xx,
yy_stat_count AS yy,
zz_stat_count AS zz
FROM dataquery
PIVOT (
SUM (statcount) AS stat_count
FOR (my_status)
IN ('XX' AS XX,
'YY' AS yy,
'ZZ' AS zz
)
)
)
select * from pivotquery;
This gives the expected results:
FIELD1 FIELD2 FIELD3 XX YY ZZ
AAA BBB CCC 11 22 33
If I have extra columns in the data, however, it does not ignore them and roll things up as I had expected. It seems to be trying to use the unused columns as part of the grouping logic, rather than ignoring them. If I change the input to this:
with dataquery (field1, field2, field3, my_status, statcount, cnt2, cnt3, cnt4) as (
select 'AAA', 'BBB', 'CCC', 'XX', 11, 12, 13, 14 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'YY', 22, 22, 23, 24 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'ZZ', 33, 32, 33, 34 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'YY', 122, 32, 33, 34 as cnt4 from dual
)
I get this:
FIELD1 FIELD2 FIELD3 XX YY ZZ
AAA BBB CCC 22
AAA BBB CCC 122 33
AAA BBB CCC 11
Instead of the expected
FIELD1 FIELD2 FIELD3 XX YY ZZ
AAA BBB CCC 11 144 33
If I change the input data further, so there is no repetition of those other fields, it shows up as 4 lines:
with dataquery (field1, field2, field3, my_status, statcount, cnt2, cnt3, cnt4) as (
select 'AAA', 'BBB', 'CCC', 'XX', 11, 12, 13, 14 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'YY', 22, 22, 23, 24 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'ZZ', 33, 32, 33, 34 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'YY', 122, 132, 33, 34 as cnt4 from dual
)
gives
FIELD1 FIELD2 FIELD3 XX YY ZZ
AAA BBB CCC 22
AAA BBB CCC 33
AAA BBB CCC 122
AAA BBB CCC 11
Is this expected behavior?
I can work around it, either by adding a subquery beforehand that selects just field1, field2, field3, mystatus and statcount to be processed by the PIVOT, or doing a select / group by afterward that gets rolls things up.

Is this expected behavior?
Yes, from the SELECT documentation:
The pivot_clause computes the aggregation functions specified at the beginning of the clause. Aggregation functions must specify a GROUP BY clause to return multiple values, yet the pivot_clause does not contain an explicit GROUP BY clause. Instead, the pivot_clause performs an implicit GROUP BY. The implicit grouping is based on all the columns not referred to in the pivot_clause, along with the set of values specified in the pivot_in_clause.). If you specify more than one aggregation function, then you must provide aliases for at least all but one of the aggregation functions.
To solve it, use a sub-query to only SELECT the required columns so that the other columns are not used implicitly as the GROUP BY clause:
with dataquery (field1, field2, field3, my_status, statcount, cnt2, cnt3, cnt4) as (
select 'AAA', 'BBB', 'CCC', 'XX', 11, 12, 13, 14 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'YY', 22, 22, 23, 24 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'ZZ', 33, 32, 33, 34 as cnt4 from dual
union
select 'AAA', 'BBB', 'CCC', 'YY', 122, 132, 33, 34 as cnt4 from dual
)
SELECT field1,
field2,
field3,
xx_stat_count AS xx,
yy_stat_count AS yy,
zz_stat_count AS zz
FROM (
SELECT field1,
field2,
field3,
my_status,
statcount
FROM dataquery
)
PIVOT (
SUM (statcount) AS stat_count
FOR (my_status) IN (
'XX' AS XX,
'YY' AS yy,
'ZZ' AS zz
)
);
Outputs:
FIELD1
FIELD2
FIELD3
XX
YY
ZZ
AAA
BBB
CCC
11
144
33
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>

Use listagg to group all students for teacher, course, semster

I have a relationship where a teacher -> courses -> enrollmenr-> students. I am trying to use listagg to get all the students first/last names on the same line as the teacher_id and course_id. In addition, I want to add the count of each teacher_id, course_id, semester.
Below is my test CASE, which has the tables, data and a part of the query. I would appreciate any help completing the query. Thanks in advance to all who answer.
CREATE TABLE teachers(teacher_id, first_name, last_name) AS
SELECT 101, 'Keith', 'Stein' FROM dual UNION ALL
SELECT 102, 'Roger', 'Wood' FROM dual UNION ALL
SELECT 103, 'Douglas', 'Kern' FROM dual UNION ALL
SELECT 104, 'Paul', 'Weber' FROM dual UNION ALL
SELECT 105, 'Jeffrey', 'Lebowitz' FROM dual UNION ALL
SELECT 106, 'Carol', 'Seltzer' FROM dual;
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 99, 'Jill', 'Coralnick' FROM dual;
CREATE TABLE courses(course_id, course_name, teacher_id, semester) AS
SELECT 1, 'Geometry', 101, '2022-2' FROM DUAL UNION ALL
SELECT 2, 'Trigonometry', 102, '2022-2' FROM DUAL UNION ALL
SELECT 3, 'Calculus', 103, '2022-2' FROM DUAL UNION ALL
SELECT 4, 'Chemistry', 104, '2022-2' FROM DUAL UNION ALL
SELECT 5, 'Biology', 105, '2022-2' FROM DUAL UNION ALL
SELECT 6, 'Physcology', 106, '2022-2' FROM DUAL;
CREATE TABLE enrollment(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 99, 3 FROM dual;
/* list all teachers, courses, student count, all students for teacher_id, course_id, semester
*/
SELECT
t.teacher_id
, t.first_name
, t.last_name
, c.course_id
, c.course_name
, c.semester
FROM teachers t
LEFT JOIN courses c
ON t.teacher_id = c.teacher_id
ORDER BY teacher_id;
TEACHER_ID FIRST_NAME LAST_NAME COURSE_ID COURSE_NAME SEMESTER
101 Keith Stein 1 Geometry 2022-2
102 Roger Wood 2 Trigonometry 2022-2
103 Douglas Kern 3 Calculus 2022-2
104 Paul Weber 4 Chemistry 2022-2
105 Jeffrey Lebowitz 5 Biology 2022-2
106 Carol Seltzer 6 Physcology 2022-2
You can use a correlated sub-query:
SELECT t.teacher_id
, t.first_name
, t.last_name
, c.course_id
, c.course_name
, c.semester
, (
SELECT LISTAGG(s.last_name || ', ' || s.first_name, '; ')
WITHIN GROUP (ORDER BY s.last_name, s.first_name)
FROM enrollment e
INNER JOIN students s
ON (e.student_id = s.student_id)
WHERE e.course_id = c.course_id
) AS students
FROM teachers t
LEFT JOIN courses c
ON t.teacher_id = c.teacher_id
ORDER BY
teacher_id;
Which, for the sample data, outputs:
TEACHER_ID
FIRST_NAME
LAST_NAME
COURSE_ID
COURSE_NAME
SEMESTER
STUDENTS
101
Keith
Stein
1
Geometry
2022-2
Aaron, Faith; Altman, Leslee; Cooper, Beth; Kern, Patty; Saladino, Lisa
102
Roger
Wood
2
Trigonometry
2022-2
Aaron, Faith; Altman, Leslee; Cooper, Beth; Kern, Patty; Saladino, Lisa
103
Douglas
Kern
3
Calculus
2022-2
Aaron, Faith; Altman, Leslee; Cooper, Beth; Coralnick, Jill; Kern, Patty; Saladino, Lisa
104
Paul
Weber
4
Chemistry
2022-2
null
105
Jeffrey
Lebowitz
5
Biology
2022-2
null
106
Carol
Seltzer
6
Physcology
2022-2
null
Or you can use JOINs and aggregate:
SELECT t.teacher_id
, MAX(t.first_name) AS first_name
, MAX(t.last_name) AS last_name
, c.course_id
, MAX(c.course_name) AS course_name
, MAX(c.semester) AS semester
, LISTAGG(
NVL2(s.student_id, s.last_name || ', ' || s.first_name, NULL),
'; '
) WITHIN GROUP (ORDER BY s.last_name, s.first_name) AS students
, COUNT(s.student_id) AS num_students
FROM teachers t
LEFT OUTER JOIN courses c
ON t.teacher_id = c.teacher_id
LEFT OUTER JOIN (
enrollment e
INNER JOIN students s
ON (e.student_id = s.student_id)
)
ON (e.course_id = c.course_id)
GROUP BY
t.teacher_id,
c.course_id
ORDER BY
t.teacher_id,
c.course_id;
db<>fiddle here

how to make into an array and also count

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

oracle query to identify some condition based overlapping date periods

I have a data set with overlapping dates in a slowly changing dimension table. I am trying to come up with a query to find out the persons with overlapping date ranges based on few conditions.
for example
PERSON_ID,RELATION_ID,RELATION_NAME,START_DT, END_DT, ACTIVE_IND,LATEST_ROW
STEVE -1 NONE 7/26/2018 10/24/2018 Y N
STEVE 111 STUDENT 8/8/2018 10/22/2018 Y N
STEVE 111 STUDENT 10/23/2018 12/31/4712 Y Y
STEVE -1 NONE 10/25/2018 2/7/2019 Y N
STEVE 222 Manager 8/9/2018 12/31/4712 Y Y
BOB 333 ASSOCIATE 8/9/2018 12/31/4712 Y Y
BOB 666 TEACHER 8/10/2018 12/31/4712 Y Y
Dave 555 Director 8/9/2018 12/31/4712 Y Y
I want to find the person_ids with overlapping date ranges only when atleast one of the relationship_id is -1. for example, BOB is both associate and teacher for overlapping periods. its ok to have 2 roles for a person. However, i want to find out all persons with overlapping dates which involves "None" relationship and some other role. Example: steve.
Could you please give me some suggestions as to how to come up with a query
Took a shot using the pattern recognition on it, and it shows all rows that overlap one another. In this case one row overlapped twice in RELATION_IDs = -1, so it displays the same row on both overlapping situations, and the query marks it as overlap_num 1 and 2.
with t (PERSON_ID,RELATION_ID,RELATION_NAME,START_DT, END_DT, ACTIVE_IND,LATEST_ROW) as (
select 'STEVE', -1, 'NONE', to_date('7/26/2018', 'MM/DD/YYYY'), to_date('10/24/2018', 'MM/DD/YYYY'), 'Y', 'N' from dual union all
select 'STEVE', 111, 'STUDENT', to_date('8/8/2018', 'MM/DD/YYYY'), to_date('10/22/2018', 'MM/DD/YYYY'), 'Y', 'N' from dual union all
select 'STEVE', 111, 'STUDENT', to_date('10/23/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual union all
select 'STEVE', -1, 'NONE', to_date('10/25/2018', 'MM/DD/YYYY'), to_date('2/7/2019', 'MM/DD/YYYY'), 'Y', 'N' from dual union all
select 'STEVE', 222, 'Manager', to_date('8/9/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual union all
select 'BOB', 333, 'ASSOCIATE', to_date('8/9/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual union all
select 'BOB', 666, 'TEACHER', to_date('8/10/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual union all
select 'Dave', 555, 'Director', to_date('8/9/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual)
select person_id, var_match, overlap_num, relation_id, start_dt, end_dt, start_dt_overlap, end_dt_overlap
from t
match_recognize (
partition by person_id order by start_dt
measures
classifier() as var_match,
match_number() as overlap_num,
last(start_dt) as start_dt_overlap,
first(end_dt) as end_dt_overlap
all rows per match
after match skip to last ovlap
pattern (strt ovlap+)
define ovlap as (strt.relation_id = -1 and start_dt <= strt.end_dt)
or (ovlap.relation_id = -1 and strt.start_dt <= prev(strt.end_dt))
)
order by person_id, overlap_num, start_dt;
Sample execution:
FSITJA#db01 2019-07-11 14:55:17> with t (PERSON_ID,RELATION_ID,RELATION_NAME,START_DT, END_DT, ACTIVE_IND,LATEST_ROW) as (
2 select 'STEVE', -1, 'NONE', to_date('7/26/2018', 'MM/DD/YYYY'), to_date('10/24/2018', 'MM/DD/YYYY'), 'Y', 'N' from dual union all
3 select 'STEVE', 111, 'STUDENT', to_date('8/8/2018', 'MM/DD/YYYY'), to_date('10/22/2018', 'MM/DD/YYYY'), 'Y', 'N' from dual union all
4 select 'STEVE', 111, 'STUDENT', to_date('10/23/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual union all
5 select 'STEVE', -1, 'NONE', to_date('10/25/2018', 'MM/DD/YYYY'), to_date('2/7/2019', 'MM/DD/YYYY'), 'Y', 'N' from dual union all
6 select 'STEVE', 222, 'Manager', to_date('8/9/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual union all
7 select 'BOB', 333, 'ASSOCIATE', to_date('8/9/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual union all
8 select 'BOB', 666, 'TEACHER', to_date('8/10/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual union all
9 select 'Dave', 555, 'Director', to_date('8/9/2018', 'MM/DD/YYYY'), to_date('12/31/4712', 'MM/DD/YYYY'), 'Y', 'Y' from dual)
10 select person_id, var_match, overlap_num, relation_id, start_dt, end_dt, start_dt_overlap, end_dt_overlap
11 from t
12 match_recognize (
13 partition by person_id order by start_dt
14 measures
15 classifier() as var_match,
16 match_number() as overlap_num,
17 last(start_dt) as start_dt_overlap,
18 first(end_dt) as end_dt_overlap
19 all rows per match
20 after match skip to last ovlap
21 pattern (strt ovlap+)
22 define ovlap as (strt.relation_id = -1 and start_dt <= strt.end_dt)
23 or (ovlap.relation_id = -1 and strt.start_dt <= prev(strt.end_dt))
24 )
25 order by person_id, overlap_num, start_dt;
PERSO VAR_MATCH OVERLAP_NUM RELATION_ID START_DT END_DT START_DT_OVERLAP END_DT_OVERLAP
----- ---------- ----------- ----------- ------------------- ------------------- ------------------- -------------------
STEVE STRT 1 -1 2018-07-26 00:00:00 2018-10-24 00:00:00 2018-07-26 00:00:00 2018-10-24 00:00:00
STEVE OVLAP 1 111 2018-08-08 00:00:00 2018-10-22 00:00:00 2018-08-08 00:00:00 2018-10-24 00:00:00
STEVE OVLAP 1 222 2018-08-09 00:00:00 4712-12-31 00:00:00 2018-08-09 00:00:00 2018-10-24 00:00:00
STEVE OVLAP 1 111 2018-10-23 00:00:00 4712-12-31 00:00:00 2018-10-23 00:00:00 2018-10-24 00:00:00
STEVE STRT 2 111 2018-10-23 00:00:00 4712-12-31 00:00:00 2018-10-23 00:00:00 4712-12-31 00:00:00
STEVE OVLAP 2 -1 2018-10-25 00:00:00 2019-02-07 00:00:00 2018-10-25 00:00:00 4712-12-31 00:00:00
6 rows selected.
Oracle Setup:
CREATE TABLE table_name ( PERSON_ID, RELATION_ID, RELATION_NAME, START_DT, END_DT, ACTIVE_IND,LATEST_ROW ) AS
SELECT 'STEVE', -1, 'NONE', DATE '2018-07-26', DATE '2018-10-24', 'Y', 'N' FROM DUAL UNION ALL
SELECT 'STEVE', 111, 'STUDENT', DATE '2018-08-08', DATE '2018-10-22', 'Y', 'N' FROM DUAL UNION ALL
SELECT 'STEVE', 111, 'STUDENT', DATE '2018-10-23', DATE '4712-12-31', 'Y', 'Y' FROM DUAL UNION ALL
SELECT 'STEVE', -1, 'NONE', DATE '2018-10-25', DATE '2019-02-07', 'Y', 'N' FROM DUAL UNION ALL
SELECT 'STEVE', 222, 'Manager', DATE '2018-08-09', DATE '4712-12-31', 'Y', 'Y' FROM DUAL UNION ALL
SELECT 'BOB', 333, 'ASSOCIATE', DATE '2018-08-09', DATE '4712-12-31', 'Y', 'Y' FROM DUAL UNION ALL
SELECT 'BOB', 666, 'TEACHER', DATE '2018-08-10', DATE '4712-12-31', 'Y', 'Y' FROM DUAL UNION ALL
SELECT 'Dave', 555, 'Director', DATE '2018-08-09', DATE '4712-12-31', 'Y', 'Y' FROM DUAL;
Query:
SELECT DISTINCT
PERSON_ID
FROM table_name t
WHERE RELATION_ID = -1
AND EXISTS (
SELECT 1
FROM table_name o
WHERE t.person_id = o.person_id
AND t.end_dt > o.start_dt
AND t.start_dt < o.end_dt
AND o.relation_id <> -1
)
Output:
| PERSON_ID |
| :-------- |
| STEVE |
db<>fiddle here

Oracle order by numeric and alpha ex: 1, 1a, 1b, 2, 2c

I have this data :
1
10
100
101
102
12
120
1a
1b
1c
2
3
4
and I would like to order like this:
1
1a
1b
1c
2
3
4
10
12
100
101
102
120
It's possible in Oracle ?
I tried this but it does not work with alphabetic characters
order by case when replace(translate(trim(COLUMN),'0123456789','0'),'0','') is null then to_number(COLUMN) end asc, COLUMN asc
If you don't mind the minor performance hit, a regexp is an easy solution.
-- sample data
with t as (select '1' as x from dual
union select '10' from dual
union select '100' from dual
union select '101' from dual
union select '102' from dual
union select '12' from dual
union select '120' from dual
union select '1a' from dual
union select '1b' from dual
union select '1c' from dual
union select '2' from dual
union select '3' from dual
union select '4' from dual)
-- query
select x
from t
-- order first by the leading numbers, then alphabetically
order by to_number(regexp_substr(x, '[0-9]*')), x;

Resources