How to query Data Group by with Order by in Oracle - oracle

I have sample data like this
CREATE TABLE table_name (aktif, "START", "END", NO_BOX, QTY) AS
SELECT 1, 'A0001', 'A0020', 2016, 100 FROM DUAL UNION ALL
SELECT 1, 'A0021', 'A0040', 2016, 100 FROM DUAL UNION ALL
SELECT 1, 'A0041', 'A0060', 2016, 100 FROM DUAL UNION ALL
SELECT 0, 'A0061', 'A0080', NULL, 100 FROM DUAL UNION ALL
SELECT 0, 'A0081', 'A0100', NULL, 100 FROM DUAL UNION ALL
SELECT 1, 'A0101', 'A0120', 2016, 100 FROM DUAL UNION ALL
SELECT 1, 'A0121', 'A0140', 2016, 100 FROM DUAL UNION ALL
SELECT 1, 'A0141', 'A0160', 2016, 100 FROM DUAL UNION ALL
SELECT 0, 'A0161', 'A0180', NULL, 100 FROM DUAL UNION ALL
SELECT 0, 'A0181', 'A0200', NULL, 100 FROM DUAL;
I want to group by the columns where AKTIF and NO_BOX remain the same based on the order of the rows and then select SUM(QTY), MIN(START), MAX(END).
The output should be:
AKTIF
START
END
NO_BOX
QTY
1
A0001
A0060
2016
300
0
A0061
A0100
NULL
200
1
A0101
A0160
2016
300
0
A0161
A0200
NULL
200

At the end, that's gaps and islands problem.
Sample data:
SQL> with test (aktif, cstart, end, no_box, qty) as
2 (select 1, 'A0001', 'A0020', 2016, 100 from dual union all
3 select 1, 'A0021', 'A0040', 2016, 100 from dual union all
4 select 1, 'A0041', 'A0060', 2016, 100 from dual union all
5 --
6 select 0, 'A0061', 'A0080', null, 100 from dual union all
7 select 0, 'A0081', 'A0100', null, 100 from dual union all
8 --
9 select 1, 'A0101', 'A0120', 2016, 100 from dual union all
10 select 1, 'A0121', 'A0140', 2016, 100 from dual union all
11 select 1, 'A0141', 'A0160', 2016, 100 from dual union all
12 --
13 select 0, 'A0161', 'A0180', null, 100 from dual union all
14 select 0, 'A0181', 'A0200', null, 100 from dual
15 ),
Query begins here:
16 temp as
17 (select t.*,
18 row_number() over (order by cstart) -
19 row_Number() over (partition by aktif order by cstart) grp
20 from test t
21 )
22 select aktif,
23 min(cstart) cstart,
24 max(end) end,
25 no_box,
26 sum(qty) qty
27 from temp
28 group by aktif, no_box, grp
29 order by cstart;
AKTIF CSTAR END NO_BOX QTY
---------- ----- ----- ---------- ----------
1 A0001 A0060 2016 300
0 A0061 A0100 200
1 A0101 A0160 2016 300
0 A0161 A0200 200
SQL>

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 query to keep looking until value is not 0 anymore

I am using Oracle 11.
I have 2 tables
TblA with columns id, entity_id and effective_date.
TblADetail with columns id and value.
If Value = 0 for the effective date, I want to keep looking for the next effective date until I found value <> 0 anymore.
The below query only look for value on 3/10/21.
If value = 0, I want to look for value on 3/11/21. If that's not 0, I want to stop.
But, if that's 0, I want to look for value on 3/12/21. If that's not 0, I want to stop.
But, if that's 0, I want to keep looking until value is not 0.
How can I do that ?
SELECT SUM(pd.VALUE)
FROM TblA p,TblADetail pd
WHERE p.id = pd.id
AND p.effective_date = to_date('03/10/2021','MM/DD/YYYY')
AND TRIM (p.entity_id) = 123
Sample data:
TblA
id entity_id effective_date
1 123 3/10/21
2 123 3/11/21
3 123 3/12/21
TblADetail
id value
1 -136
1 136
2 2000
3 3000
In the above data, for entity_id 123, starting from effective_date 3/10/21, I would like to to return value 2000 (from TblADetail) effective_date 3/11/21.
So, starting from a certain date, I want the results from the minimum date that has non-zero values.
Thank you.
You can do what you need to do by grouping the sum on the effective date, and using the MIN analytic function to find the earliest date. Once you've done that, you simply need to select the date that matches the earliest date.
E.g.:
with tbla as (select 1 id, ' 123' entity_id, to_date('10/03/2021', 'dd/mm/yyyy') effective_date from dual union all
select 2 id, ' 123' entity_id, to_date('11/03/2021', 'dd/mm/yyyy') effective_date from dual union all
select 3 id, ' 123' entity_id, to_date('12/03/2021', 'dd/mm/yyyy') effective_date from dual),
tbla_detail as (select 1 id, -136 value from dual union all
select 1 id, 136 value from dual union all
select 2 id, 2000 value from dual union all
select 3 id, 3000 value from dual),
results as (select a.effective_date,
sum(ad.value) sum_value,
min(case when sum(ad.value) != 0 then a.effective_date end) over () min_effective_date
from tbla a
inner join tbla_detail ad on a.id = ad.id
where a.effective_date >= to_date('10/03/2021', 'dd/mm/yyyy')
and trim(a.entity_id) = '123'
group by a.effective_date)
select sum_value
from results
where effective_date = min_effective_date;
SUM_VALUE
----------
2000
Straightforward; read comments within code. Sample data in lines #1 - 13, query begins at line #14.
SQL> with
2 -- sample data
3 tbla (id, entity_id, effective_date) as
4 (select 1, 123, date '2021-03-10' from dual union all
5 select 2, 123, date '2021-03-11' from dual union all
6 select 3, 123, date '2021-03-12' from dual
7 ),
8 tblb (id, value) as
9 (select 1, -136 from dual union all
10 select 1, 136 from dual union all
11 select 2, 2000 from dual union all
12 select 3, 3000 from dual
13 ),
14 tblb_temp as
15 -- simple grouping per ID
16 (select id, sum(value) value
17 from tblb
18 group by id
19 )
20 -- return TBLA values whose ID equals TBLB_TEMP's minimum ID
21 -- whose value isn't zero
22 select a.id, a.entity_id, a.effective_date
23 from tbla a
24 where a.id = (select min(b.id)
25 from tblb_temp b
26 where b.value > 0
27 );
ID ENTITY_ID EFFECTIVE_
---------- ---------- ----------
2 123 03/11/2021
SQL>

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;

Oracle - creating a group based on group spacing

How to make a query that will create groups that have a space between them greater than "n"?
Data:
01-01-2000
02-01-2000
03-01-2000
06-01-2000
07-01-2000
19-02-2001
10-01-2002
11-01-2002
I would like to get a result for the interval between records, e.g. 2 days:
DATE GROUP
01-01-2000 1
02-01-2000 1
03-01-2000 1
06-01-2000 2
07-01-2000 2
19-02-2001 3
10-01-2002 4
11-01-2002 4
For 10 days:
01-01-2000 1
02-01-2000 1
03-01-2000 1
06-01-2000 1
07-01-2000 1
19-02-2001 2
10-01-2002 3
11-01-2002 3
Another example with integers:
with x as (
select 1 as A from dual
union all
select 2 as A from dual
union all
select 3 as A from dual
union all
select 10 as A from dual
union all
select 20 as A from dual
union all
select 22 as A from dual
union all
select 33 as A from dual
union all
select 40 as A from dual
union all
select 50 as A from dual
union all
select 100 as A from dual
union all
select 101 as A from dual
union all
select 102 as A from dual
) select A
from x;
I need to create groups for a value increase of more than 3:
Example result:
1 1
2 1
3 1
10 2
20 3
22 3
33 4
40 5
50 6
100 7
101 7
102 7
Here is one way to do it
CREATE TABLE TEST (
DATE_IN DATE
);
INSERT INTO TEST VALUES (TO_DATE('01-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('02-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('03-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('06-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('07-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('19-02-2001','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('10-01-2002','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('11-01-2002','DD-MM-YYYY'));
--HERE IS AN EXAMPLE FOR 1 DAY. Just change the value in the > 1 TO >10
--if you want to create a group if there is a gap of more than 10days
SELECT DATE_IN, SUM(NEW_GROUP) OVER ( ORDER BY DATE_IN) AS GROUPE FROM (
SELECT
DATE_IN,
CASE WHEN DATE_IN - LAG(DATE_IN,1,TO_DATE('01-01-1900','DD-MM-YYYY')) OVER ( ORDER BY DATE_IN) > 1 THEN 1 ELSE 0 END AS NEW_GROUP
FROM TEST
)
-- Result
DATE_IN GROUPE
2000-01-01T00:00:00Z 1
2000-01-02T00:00:00Z 1
2000-01-03T00:00:00Z 1
2000-01-06T00:00:00Z 2
2000-01-07T00:00:00Z 2
2001-02-19T00:00:00Z 3
2002-01-10T00:00:00Z 4
2002-01-11T00:00:00Z 4
Example with integer:
with x as (
select 1 as A from dual
union all
select 2 as A from dual
union all
select 3 as A from dual
union all
select 10 as A from dual
union all
select 20 as A from dual
union all
select 22 as A from dual
union all
select 33 as A from dual
union all
select 40 as A from dual
union all
select 50 as A from dual
union all
select 100 as A from dual
union all
select 101 as A from dual
union all
select 102 as A from dual
) SELECT A, SUM(NEW_GROUP) OVER ( ORDER BY A) AS GROUPE FROM (
SELECT
A,
CASE WHEN A - LAG(A,1,1) OVER ( ORDER BY A) > 5 THEN 1 ELSE 0 END AS NEW_GROUP
FROM X
)
order by A;

oracle select two count values in one row

I need help with this select in oracle. I have table with 2 columns: (table > date, value) for example:
1.1.2017, 16
1.1.2017, 16
1.1.2017, 16
1.1.2017, 17
1.2.2017, 16
1.2.2017, 16
1.2.2017, 17
1.2.2017, 17
1.3.2017, 16
Result must be:
1.1.2017 as date, 3 as count of 16, 1 as count of 17
1.2.2017, 2, 2
1.3.2017, 1, 0
Current SQL:
select date, count(value) from table group by date, value
However, this does not return the same date with one row with count of both values.
You need conditional counting, something like this:
with
your_table ( dt, value ) as (
select to_date('1.1.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.1.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.1.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.1.2017', 'mm.dd.yyyy'), 17 from dual union all
select to_date('1.2.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.2.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.2.2017', 'mm.dd.yyyy'), 17 from dual union all
select to_date('1.2.2017', 'mm.dd.yyyy'), 17 from dual union all
select to_date('1.3.2017', 'mm.dd.yyyy'), 16 from dual
)
-- end of test data; solution (SQL query) begins below this line
select dt, count(case value when 16 then 'x' end) as ct_16,
count(case value when 17 then 'x' end) as ct_17
from your_table
group by dt;
DT CT_16 CT_17
---------- ----- -----
01/01/2017 3 1
01/02/2017 2 2
01/03/2017 1 0
select date, value, count(value) from table group by date, value;

Resources